一、Express安装

Express是一个node.js模块,采用npm全局模块。

npm install -g express

二、新建项目

创建一个项目express testExpress,会自动生成目录。

   create : testExpress
   create : testExpress/package.json
   create : testExpress/app.js
   create : testExpress/public
   create : testExpress/public/images
   create : testExpress/views
   create : testExpress/views/layout.jade
   create : testExpress/views/index.jade
   create : testExpress/routes
   create : testExpress/routes/index.js
   create : testExpress/routes/user.js
   create : testExpress/public/stylesheets
   create : testExpress/public/stylesheets/style.css
   create : testExpress/public/javascripts

运行node app.js (运行程序,默认地址是http://localhost:3000) 如果打开页面出错,可能你没有安装jade模块,那就输入npm install jade进行安装,在我们 日志分析系统中没有用jade模板,用的ejs模板。

三、express项目目录文件介绍

Express目录介绍:

目录/文件	                                  说明

package.json	 						npm依赖配置文件, java Maven中的pom.xml文件
app.js                                  项目的入口文件
routes/                                 用于存放路由文件
public/                                 静态文件
javascript/                             js
stylesheets/                            css
images/                                 图片
views/                                  模板文件, express默认采用jade
node_modules/                           存放npm安装到本地依赖包,依赖包在package.json文件
										中声明,使用npm install指令安装

申明:

以上目录文件介绍仅对于express框架自动生成目录,框架没有除了package.json文件和 node_modules目录,
没有限定其他文件名称和目录结构,大家在实际开发中可以根据项目需要重新定义目录结构。

四、运行原理

4.1 底层:http模块

Express框架建立在node.js内置的http模块上。http模块生成服务器的原始代码如下:

var http = require("http");

var app = http.createServer(function(request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("Hello world!\n");
});

app.listen(3000, "localhost");
console.log("Server running at http://localhost:3000/");

上面代码的关键是http模块的createServer方法,表示生成一个HTTP服务器实例。该方法接受一个回调函数,该回调函数的参数,分别为代表HTTP请求和HTTP回应的request对象和response对象。

4.2 对http模块的再包装

Express框架的核心是对http模块的再包装。上面的代码用Express改写如下:

var express = require("express");
var http = require("http");

var app = express();

app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

http.createServer(app).listen(3000);

比较两段代码,可以看到它们非常接近,唯一的差别是createServer方法的参数,从一个回调函数变成了一个Epress对象的实例。而这个实例使用了use方法,加载了与上一段代码相同的回调函数。

Express框架等于在http模块之上,加了一个中间层,而use方法则相当于调用中间件。

4.3 中间件

简单说,中间件(middleware)就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、分析数据、以及其他在需要最终将数据发送给用户之前完成的任务。它最大的特点就是,一个中间件处理完,再传递给下一个中间件。

node.js的内置模块http的createServer方法,可以生成一个服务器实例,该实例允许在运行过程中,调用一系列函数(也就是中间件)。当一个HTTP请求进入服务器,服务器实例会调用第一个中间件,完成后根据设置,决定是否再调用下一个中间件。中间件内部可以使用服务器实例的response对象(ServerResponse,即回调函数的第二个参数),以及一个next回调函数(即第三个参数)。每个中间件都可以对HTTP请求(request对象)做出回应,并且决定是否调用next方法,将request对象再传给下一个中间件。

一个不进行任何操作、只传递request对象的中间件,大概是下面这样:

function uselessMiddleware(req, res, next) { 
	next();
}

上面代码的next为中间件的回调函数。如果它带有参数,则代表抛出一个错误,参数为错误文本。

function uselessMiddleware(req, res, next) { 
	next('出错了!');
} 抛出错误以后,后面的中间件将不再执行,直到发现一个错误处理函数为止。

4.4 use方法

use是express调用中间件的方法,它返回一个函数。下面是一个连续调用两个中间件的例子:

var express = require("express");
var http = require("http");

var app = express();

app.use(function(request, response, next) {
  console.log("In comes a " + request.method + " to " + request.url);
  next();
});

app.use(function(request, response) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  response.end("Hello world!\n");
});

http.createServer(app).listen(3000);

上面代码先调用第一个中间件,在控制台输出一行信息,然后通过next方法,调用第二个中间件,输出HTTP回应。由于第二个中间件没有调用next方法,所以不再request对象就不再向后传递了。

使用use方法,可以根据请求的网址,返回不同的网页内容。

var express = require("express");
var http = require("http");

var app = express();

app.use(function(request, response, next) {
  if (request.url == "/") {
    response.writeHead(200, { "Content-Type": "text/plain" });
    response.end("Welcome to the homepage!\n");
  } else {
    next();
  }
});

app.use(function(request, response, next) {
  if (request.url == "/about") {
    response.writeHead(200, { "Content-Type": "text/plain" });
  } else {
    next();
  }
});

app.use(function(request, response) {
  response.writeHead(404, { "Content-Type": "text/plain" });
  response.end("404 error!\n");
});

http.createServer(app).listen(3000);

上面代码通过request.url属性,判断请求的网址,从而返回不同的内容。

除了在回调函数内部,判断请求的网址,Express也允许将请求的网址写在use方法的第一个参数。

app.use('/', someMiddleware);

上面代码表示,只对根目录的请求,调用某个中间件。

五、Express的方法

5.1 all方法和HTTP动词方法

针对不同的请求,Express提供了use方法的一些别名。比如,上面代码也可以用别名的形式来写。

var express = require("express");
var http = require("http");
var app = express();

app.all("*", function(request, response, next) {
  response.writeHead(200, { "Content-Type": "text/plain" });
  next();
});

app.get("/", function(request, response) {
  response.end("Welcome to the homepage!");
});

app.get("/about", function(request, response) {
  response.end("Welcome to the about page!");
});

app.get("*", function(request, response) {
  response.end("404!");
});

http.createServer(app).listen(3000);

上面代码的all方法表示,所有请求都必须通过该中间件,参数中的“*”表示对所有路径有效。get方法则是只有GET动词的HTTP请求通过该中间件,它的第一个参数是请求的路径。由于get方法的回调函数没有调用next方法,所以只要有一个中间件被调用了,后面的中间件就不会再被调用了。

除了get方法以外,Express还提供post、put、delete方法,即HTTP动词都是Express的方法。

这些方法的第一个参数,都是请求的路径。除了绝对匹配以外,Express允许模式匹配:

app.get("/hello/:who", function(req, res) {
  res.end("Hello, " + req.params.who + ".");
});

上面代码将匹配“/hello/alice”网址,网址中的alice将被捕获,作为req.params.who属性的值。需要注意的是,捕获后需要对网址进行检查,过滤不安全字符,上面的写法只是为了演示,生产中不应这样直接使用用户提供的值。

如果在模式参数后面加上问号,表示该参数可选:

app.get('/hello/:who?',function(req,res) {
if(req.params.id) {
    res.end("Hello, " + req.params.who + ".");
}
else {
    res.send("Hello, Guest.");
}
});

5.2 set方法

set方法用于指定变量的值。

app.set("views", __dirname + "/views");
app.set("view engine", "jade");

上面代码使用set方法,为系统变量“views”和“view engine”指定值。

5.3 response对象

(1)response.redirect方法

response.redirect方法允许网址的重定向。

response.redirect("/hello/anime");
response.redirect("http://www.example.com");
response.redirect(301, "http://www.example.com"); 

(2)response.sendFile方法

response.sendFile方法用于发送文件。

response.sendFile("/path/to/anime.mp4");

(3)response.render方法

response.render方法用于渲染网页模板。

app.get("/", function(request, response) {
  response.render("index", { message: "Hello World" });
});

上面代码使用render方法,将message变量传入index模板,渲染成HTML网页。

5.4 requst对象

(1)request.ip

request.ip属性用于获得HTTP请求的IP地址。

(2)request.files

request.files用于获取上传的文件。

六、日志系统部分配置

// should be placed before express.static
app.use(express.compress({
  filter: function (req, res) {
    return /json|text|javascript|css/.test(res.getHeader('Content-Type'))
  },
  level: 9
}))

app.use(express.favicon('public/favicon.ico'))
app.use(express.static(config.root + '/public'))

// don't use logger for test env
if (process.env.NODE_ENV !== 'test') {
  app.use(express.logger('dev'))
}

// set views path, template engine and default layout
  app.engine('html', require('ejs').__express)
app.set('views', config.root + '/app/views')
app.set('view engine', 'html')

app.use(partials());
app.use(pjax());


// cookieParser should be above session
app.use(express.cookieParser())


// bodyParser should be above methodOverride
app.use(express.bodyParser())
app.use(express.methodOverride())

//log4js
log.use(app);

 app.use(function(req, res, next){
	res.on('header', function() {
		if (!req.session) return;
		if (req.session.cookie.expires==null) return;
		req.session.cookie.expires = new Date(Date.now() + 1000*60*60*24*14)
	})
	next()
});

// express/mongo session storage
app.use(express.session({
  secret: 'logAnalyse-pangu',
  cookie: { maxAge: 900000 },  //15 minute
  store: new mongoStore({
    url: config.db,
    collection : 'sessions'
  })
}))



// connect flash for flash messages - should be declared after sessions
app.use(flash())

// adds CSRF support
if (process.env.NODE_ENV !== 'test') {
    app.use(express.csrf())
}

app.use(function(req, res, next){
  //if(req.url != "/faye")
      res.locals.csrf_token = req.csrfToken();
  next()
})


//menu Plug-in technology    
var menus = [];
require('../app/controllers/menu').loadMenu(config.root+'/app/controllers/menu',
function(m){menus = m;});

  
//Public Response Information 
app.use(function(req, res, next){         
    if (req.session.user){ 
        res.locals.menus = menus;
        res.locals.current_user = req.session.user;
    }else{ 
                 
       if(req.url != "/login.html" && req.url != "/auth.html" && req.url != "/logout" 
       && req.url != "/register" && req.url != "/registerAction"){
            if (req.url == "/getInbox.html"){
               ContentType = "text/plain";
               res.StatusCode =500;
               res.write("会话超时,请重新登录!");
               res.end();
               return;
            }
            return res.redirect('/login.html')
       }
    }
    return next();   
});

            
//Plug-in technology    
plugin(app).require(config.root+'/app/controllers/plugin').load();

// routes should be at the last
app.use(app.router)