Express.js中文文档-Application对象

 2016年03月13日    1190     声明


创建一个Application对象实例,也就是创建一个Express应用。app对象一般在Express应用的app.js文件中创建,通过app对象,可以实现定向HTTP请求、配置中间件、渲染HTML、配置模板等。


  1. 创建对象实例
  2. 属性
  3. 事件
  4. 方法

1. 创建对象实例

app对象通过express模块导出的顶层方法express()创建:

var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(3000);

app对象中定义了一些方法,这些方法的作用有:

对象中还有一些设置属性的方法,这些设置将影响应用的形为。详见:应用设置


2. 属性

2.1 app.locals

app.locals属性是一个对象,对象中的属性是对应用内部local变量的引用。

通过app.locals设置的属性,属性值在应用的整个生命周期内都可以使用,而通过res.locals对象设置的属性只能在当前请求中使用。

app.locals.name = 'IT笔录';
app.locals.url = 'itbilu.com';

定义属性后,我们就可以通过local变量,在当前应用所有的渲染模中访问。通过这个属性我们可以定义一些项级的数据,并在渲染模板中使用。在局部变量中,可以通过req.app.locals访问这些属性:

req.app.locals.name; // 'IT笔录'
req.app.locals.url; //'itbilu.com'


2.2 app.mountpath

应用做为子应用被安装时,app.mountpath包含了一个或多个路径模式。

var express = require('express');

var app = express(); // 主APP
var admin = express(); // 子APP

admin.get('/', function (req, res) {
  console.log(admin.mountpath); // /admin
  res.send('Admin Homepage');
});

app.use('/admin', admin); // 挂载的子应用

这类似于req对象的baseUrl属性,它不会返回req.baseUrl所匹配的路径,而是返回路径模式。

var admin = express();

admin.get('/', function (req, res) {
  console.log(admin.mountpath); // [ '/adm*n', '/manager' ]
  res.send('Admin Homepage');
});

var secret = express();
secret.get('/', function (req, res) {
  console.log(secret.mountpath); // /secr*t
  res.send('Admin Secret');
});

admin.use('/secr*t', secret); // 在'admin'子应用中,对'/secr*t'匹配结果加载'secret'路由处理器
app.use(['/adm*n', '/manager'], admin); // 在父应用中,对'/adm*n' 和 '/manager'匹配结果加载'admin'路由处理器


3. 事件

app.on('mount', callback(parent))

'mount'事件会在子应用中发生,当它被挂载到父应用时会触发'mount'事件。触发后,父应用会做回调函数参数被传调到子应用中。

var admin = express();

admin.on('mount', function (parent) {
  console.log('Admin被挂载');
  console.log(parent); // 对父应用的引用
});

admin.get('/', function (req, res) {
  res.send('Admin Homepage');
});

app.use('/admin', admin);


4. 方法

4.1 定义路由处理器:app.all()

app.all(path, callback [, callback ...])

这个方法用于设置路由,与标准路由方法app.METHOD类似,但它会匹配所有类型的HTTP请求方法

app.all()用于映射“全局的”逻辑处理,可以匹配具体路径或任意的前缀。

如,我们可以把下面两个处理定义在路由的顶部,用户所有请求都会经过这个路径,我们可以在这个路由处理器中进行验证然后加载用户信息:

app.all('*', requireAuthentication, loadUser);

也可以分开写:

app.all('*', requireAuthentication);
app.all('*', loadUser);

请注意,这些处理一般不必做为请求的终点,在处理完成后可以通过调用next()进入下一步的处理。

我们也可以匹配部分路径,并添加“全局”处理。如,对'/api'开头路径进行用户验证:

app.all('/api/*', requireAuthentication);


4.2 定义DELETE请求路由:app.delete()

app.delete(path, callback [, callback ...])

对指定路径path定义一个针对HTTP DELETE请求的处理器。

app.delete('/', function (req, res) {
  res.send('DELETE 请求主页');
});


4.3 关闭设置:app.disable()

app.disable(name)

app.disable()方法用于将指定的设置变更为false状态,其相当于调用app.set('name', false)

app.disable('trust proxy');
app.get('trust proxy');
// => false


4.4 检查设置是否关闭:app.disabled()

app.disabled(name)

检查某项设置是否是关闭状态,如果设置为关闭状态(不可用)则返回true

app.disabled('trust proxy');
// => true

app.enable('trust proxy');
app.disabled('trust proxy');
// => false


4.5 开启设置:app.enable()

app.enable(name)

app.disable()方法功能相反,用于将指定的设置变更为true状态,其相当于调用app.set('name', true)

app.enable('trust proxy');
app.get('trust proxy');
// => true


4.6 检查设置是否开启:app.enabled()

app.enabled(name)

检查某项设置是否是开启状态,如果设置为开启状态(可用)则返回true

app.enabled('trust proxy');
// => false

app.enable('trust proxy');
app.enabled('trust proxy');
// => true


4.7 设置模块引擎:app.engine()

app.engine(ext, callback)

注册模板引擎。

默认情况下,Express会根据文件扩展名require()引用模板引擎。例如,想渲染一个"foo.jade"的文件,可以像下面这样注册,注册后设置会被缓存以提高require()的效率。

app.engine('jade', require('jade').__express);

如果不想使用.__express引擎,或要对不同扩展名使用另外的引擎。如,对".html"文件添加一个ejs引擎:

app.engine('html', require('ejs').renderFile);


4.8 获取设置值:app.get()

app.get(name)

获取指定设置的值。

app.get('title');
// => undefined

app.set('title', 'My Site');
app.get('title');
// => "My Site"


4.9 设置GET请求路由:app.get()

app.get(path, callback [, callback ...])

对指定路径path定义一个针对HTTP GET请求的路由处理器。

app.get('/', function (req, res) {
  res.send('GET 请求了主页');
});


4.10 绑定监听端口:app.listen()

app.listen(port, [hostname], [backlog], [callback])

绑定并监听到指定端口和主机名的连接,这个方法等同于http.server.listen()方法。

var express = require('express');
var app = express();
app.listen(3000);

通过express()返回应用程序实际上是一个JavaScript函数,它被设计为以一个回调函数的形式传递到Node HTTP服务,以处理HTTP请求。这样就很容易在HTTP和HTTPS中使用相同的代码,因为应用的实现方式不是继承而是一个简单的回调。

var express = require('express');
var https = require('https');
var http = require('http');
var app = express();

http.createServer(app).listen(80);
https.createServer(options, app).listen(443);

app.listen()方法会返回一个http.Server对象,它的实现源码非常简单:

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};


4.11 创建路由:app.METHOD()

app.METHOD(path, callback [, callback ...])

定义一个指定HTTP请求的路由。METHOD表示HTTP请求方式(如GET、PUT、POST等)的小写形式(如app.get()app.put()app.post())。

Express支持的路由请求方式有:

  • checkout
  • connect
  • copy
  • delete
  • get
  • head
  • lock
  • merge
  • mkactivity
  • mkcol
  • move
  • m-search
  • notify
  • options
  • patch
  • post
  • propfind
  • proppatch
  • purge
  • put
  • report
  • search
  • subscribe
  • trace
  • unlock
  • unsubscribe

注意:在定义路由回调函数时,我们可以像定义中间件一样,定义多个回调函数。可以通过next()进入下一个回调函数,也可以通过next('route')方法跳过后面的回调函数。

app.all()是一个比较特别定义路由的方法,通过这个方法定义的路由将会在GET、PUT、POST等所有请求方式中起作用。

app.all('/secret', function (req, res, next) {
  console.log('Accessing the secret section ...');
  next(); // 转到下一个处理器
});


4.12 添加参数触发器:app.param()

app.param([name], callback)

将回调触发器添加到路由参数中。其中是,callback是添加的回调函数。回调函数的参数是请求对象、响应对象、下一个中间件、参数的值和参数的名称。

  • name:参数的名称或参数名数组
  • callback:要添加的回调函数。其参数形式为fn(req, res, next, value),分别为请求对象、响应对象、下一个中间件、参数的值和参数的名称。

如果name是一个数组,则会在声明的每一个参数都注册回调触发器。此外,对于每个声明的参数,除了最后一个,有会有一个next回调,用于进入声明中下一个参数的回调函数。对于最后一个参数,调用下一个next将在当前正在处理的路由中调用下一个中间件。

如,对于一个/:user路径,我们对其添加一个参数触发器,然后在这个触发器中验证用户是否存在,并将获取的结果添加到req.user中:

app.param('user', function(req, res, next, id) {

  // 获取用户信息,并将结果添加到request对象中
  User.find(id, function(err, user) {
    if (err) {
      next(err);
    } else if (user) {
      req.user = user;
      next();
    } else {
      next(new Error('failed to load user'));
    }
  });
});

所有的参数回调函数会在作何使用了参数的路由中被触发,但在同一个请求-响应周期内只被触发一次。

app.param('id', function (req, res, next, id) {
  console.log('只会调用一次');
  next();
});

app.get('/user/:id', function (req, res, next) {
  console.log('这里会被匹配到');
  next();
});

app.get('/user/:id', function (req, res) {
  console.log('这里也会被匹配到');
  res.end();
});

当GET请求/user/12时,依次输出如下:

只会调用一次
这里会被匹配到
这里也会被匹配到

参数触发器可以匹配一个数组,所有数组中的所有参数被匹配到时,都会触发回调函数一次。

app.param(['id', 'page'], function (req, res, next, value) {
  console.log('被触发一次', value);
  next();
});

app.get('/user/:id/:page', function (req, res, next) {
  console.log('这里会被匹配到');
  next();
});

app.get('/user/:id/:page', function (req, res) {
  console.log('这里也会被匹配到');
  res.end();
});

当GET请求/user/12/3时,依次输出如下:

被触发一次12
被触发一次3
这里会被匹配到
这里也会被匹配到

Express自v4.11.0以后,增加了app.param(callback);形式的参数触发器。在这种形式的参数触发器是app.param(name, callback)触发器的具体实现,它的回调函数中包含两个参数,其内部必须返回一个中间件。

var express = require('express');
var app = express();

// 自定义的 app.param()的形为
app.param(function(param, option) {
  return function (req, res, next, val) {
    if (val == option) {
      next();
    }
    else {
      res.sendStatus(403);
    }
  }
});

// 使用自定义的 app.param()
app.param('id', 1337);

// 在路由中捕获触发器
app.get('/user/:id', function (req, res) {
  res.send('OK');
});

app.listen(3000, function () {
  console.log('Ready');
});


4.13 返回规划路径:app.path()

app.path()

返回应用的规范路径。

var app = express()
  , blog = express()
  , blogAdmin = express();

app.use('/blog', blog);
blog.use('/admin', blogAdmin);

console.log(app.path()); // ''
console.log(blog.path()); // '/blog'
console.log(blogAdmin.path()); // '/blog/admin'


4.14 设置POST请求路由:app.post()

app.post(path, callback [, callback ...])

对指定路径path定义一个HTTP POST请求的路由处理器。

app.post('/', function (req, res) {
  res.send('POST 请求主页');
});


4.15 设置PUT请求路由:app.post()

app.put(path, callback [, callback ...])

对指定路径path定义一个HTTP PUT请求的路由处理器。

app.put('/', function (req, res) {
  res.send('PUT 请求主页');
});


4.16 渲染视图:app.render()

app.render(view, [locals], callback)
  • view:{String},HTML视图路径
  • locals:{Object},可选参数。传递给HTML视图数据变量
  • callback:{Function}。渲染结果,其形式为:fn(err, html)

通过回调函数返回一个渲染后的HTML视图,这个函数与res.render()类似,但不会将渲染结果返回给客户端。

app.render('email', function(err, html){
  // ...
});

app.render('email', { name: 'Tobi' }, function(err, html){
  // ...
});


4.17 返回路由单例:app.route()

app.route(path)

返回一个单一路径的实例,然后我们就可以在其中间件中处理HTTP行为。使用app.route()可以避免路由重复定义而导致错误不易定位的问题。

var app = express();

app.route('/events')
.all(function(req, res, next) {
  // 首先在所有HTTP请求中运行
  // 可以把它想象成一个路由中间件
})
.get(function(req, res, next) {
  res.json(...);
})
.post(function(req, res, next) {
  // 可以添加一个新事件
});


4.18 应用设置:app.set()

app.set(name, value)

向应用添加名称和值为namevalue的设置。可添加的设置参见应用设置表

app.set('title', 'My Site');
app.get('title'); // "My Site"


应用设置表

  • case sensitive routing:{Boolean},默认值N/A (undefined)-设置路由大小写敏感。设置为true时,"/Foo"和"/foo"不是一个路径;设置为false时,"/Foo"和"/foo"是同一个路径
  • env:{String},默认值process.env.NODE_ENV( NODE_ENV中设置的环境变量)-设置应用运行环境模式,应确认在生产环境的设置值为'production'
  • etag:{Varied},默认值weak-设置ETag响应头,可选值有:
    • {Boolean}-为true时允许使用弱ETag,这也是默认设置。
    • {String}-为'strong'时,使用强ETag头。为'weak'时,使用弱ETag头。
    • Function-使用自定义的ETag
      app.set('etag', function (body, encoding) {
        return generateHash(body, encoding); // consider the function is defined
        });
  • jsonp callback name:{String},默认值callback-指定 JSONP 回调函数名
  • json replacer:{Varied},默认值N/A (undefined)JSON.stringify函数的'replacer'参数,'replacer' 参数
  • json spaces:{Varied},默认值N/A (undefined)JSON.stringify函数的'space'参数,'space' 参数
  • query parser:{Varied},默认值'extended'-设置URL查询字符串的解析器。可选值有:
    • false-不使用解析器
    • 'simple'-使用Node.js的querystring模块解析
    • 'extended'-使用qs模块解析
    • function-自定义解板器,传入一个函数,返回值为包含查询字符串key-value的对象
  • strict routing:{Boolean},默认值N/A (undefined)-严格路由模式。设置为true时,"/foo"和"/foo/"不是同一个路径;设置为false时,"/foo"和"/foo/"是同一个路径
  • subdomain offset:{Number},默认值2-删除子域的主机'.'分隔符数量
  • trust proxy:{Varied},默认值false (disabled)-设置代理可信任的前缀。可设置值有:
    • {Boolean}-设置为true时,会将IP地址理解来自于X-Forwarded-*头。设置为false时,会将IP地址理解来自于连接req.connection.remoteaddress,这也是默认值。
    • IP addresses-一个IP地址、子网掩码或一组IP地址及其了网。预先设置的子网名为:
      • loopback - 127.0.0.1/8, ::1/128
      • linklocal - 169.254.0.0/16, fe80::/10
      • uniquelocal - 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7

      设置IP地址时,可以像下面这样设置:

      app.set('trust proxy', 'loopback') // 指定单个子网
      	app.set('trust proxy', 'loopback, 123.123.123.123') // 指定子网及地址
      	app.set('trust proxy', 'loopback, linklocal, uniquelocal') // 指定多个子网为CSV
      	app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']) // 以数组形式指定多个子网
    • Number-信任客户端的跳转级别
    • Function-设置自定义的信任方式。
      app.set('trust proxy', function (ip) {
      	    if (ip === '127.0.0.1' || ip === '123.123.123.123') return true; // 可信任的 IPs
      	    else return false;
      	  });
  • views:{String or Array},默认值process.cwd() + '/views'-设置应用的视图目录或数组。设置为数组时,视图的使用顺序会按其在视图中的出现顺序优先使用
  • view cache:{Boolean},默认值:生产环境为true其它为undefined-设置是否缓存视图。
  • view engine:{String},默认值N/A (undefined)-设置视图引擎
  • x-powered-by:{Boolean},默认值true-设置是否允许使用X-Powered-By: Express



4.19 挂载中间件:app.use()

app.use([path,] function [, function...])
  • path:{String},可选参数。挂中间件的路径
  • function:{Function}。中间件函数,可以是多个

向指定路径path添加指定的中间件(s)。如果不指定path参数,默认为'/'

app.use('/admin', function(req, res, next) {
  // GET 'http://www.example.com/admin/new'
  console.log(req.originalUrl); // '/admin/new'
  console.log(req.baseUrl); // '/admin'
  console.log(req.path); // '/new'
  next();
});

挂载中间件函数到指定路径后,中间件函数会在用户请求匹配到指定路径时自动执行中间件函数。

当向'/'路径挂载中间件后,中间件函数会在应用的所有的请求中被执行。

// 这个中间件会在应用的所有请求中执行
app.use(function (req, res, next) {
  console.log('Time: %d', Date.now());
  next();
});

中间件函数是按顺序执行的,所在中间件的添加顺序很重要。

// 这个中间件不允许请求通过它
app.use(function(req, res, next) {
  res.send('Hello World');
});

// 用户请求永远不会到达这个路径
app.get('/', function (req, res) {
  res.send('Welcome');
});


路径

路径是一个字符串,可以是一个路径、一个路径模式匹配、一个正则表达式、或一个数组。下面是一些中间件的例子:

  • 路径

    如,匹配'/abcd'路径:

    app.use('/abcd', function (req, res, next) {
      next();
    });
  • 路径模式匹配

    如,匹配'/abcd'或'/abd'路径:

    app.use('/abc?d', function (req, res, next) {
      next();
    });

    如,匹配'/abcd'、'/abbcd'或'/abbbbbcd'等路径:

    app.use('/ab+cd', function (req, res, next) {
      next();
    });

    如,匹配'/abcd'、'/abxcd'、'/abFOOcd'或'/abbArcd'等路径:

    app.use('/ab\*cd', function (req, res, next) {
      next();
    });
  • 正则表达式

    如,匹配'/abc'和'/xyz'路径:

    app.use(/\/abc|\/xyz/, function (req, res, next) {
      next();
    });
  • 数组

    如,匹配'/abcd'、'/xyza'、'/lmn'和'/pqr'路径:

    app.use(['/abcd', '/xyza', /\/lmn|\/pqr/], function (req, res, next) {
      next();
    });


中间件

中间件函数是一个函数。可以是一个函数、多个函数、函数数组或是几都的混合。

routerapp都实现在了中间件接口,所以你可以它们二者之间使用中间件。

  • 单个中间件

    在应用中使用单个中间件:

    app.use(function (req, res, next) {
      next();
    });

    将路由中作为中间件使用:

    var router = express.Router();
    router.get('/', function (req, res, next) {
      next();
    });
    app.use(router);

    var subApp = express(); subApp.get('/', function (req, res, next) { next(); }); app.use(subApp);

  • 多个中间件

    可以在一个路径中定义多个中间件:

    var r1 = express.Router();
    r1.get('/', function (req, res, next) {
      next();
    });
    
    var r2 = express.Router();
    r2.get('/', function (req, res, next) {
      next();
    });
    
    app.use(r1, r2);
  • 数组

    可以将多个中间件逻辑放到一个数组中使用:

    var r1 = express.Router();
    r1.get('/', function (req, res, next) {
      next();
    });
    
    var r2 = express.Router();
    r2.get('/', function (req, res, next) {
      next();
    });
    
    app.use('/', [r1, r2]);
  • 综合使用

    可以将多种方式一起使用:

    function mw1(req, res, next) { next(); }
    function mw2(req, res, next) { next(); }
    
    var r1 = express.Router();
    r1.get('/', function (req, res, next) { next(); });
    
    var r2 = express.Router();
    r2.get('/', function (req, res, next) { next(); });
    
    var subApp = express();
    subApp.get('/', function (req, res, next) { next(); });
    
    app.use(mw1, [mw2, r1, r2], subApp);