- Application
- app.set(name, value)
- app.get(name)
- app.enable(name)
- app.disable(name)
- app.enabled(name)
- app.disabled(name)
- app.configure([env], callback)
- app.use([path], function)
- settings
- app.engine(ext, callback)
- app.param([name], callback)
- app.VERB(path, [callback…], callback)
- app.all(path, [callback…], callback)
- app.locals
- app.render(view, [options], callback)
- app.routes
- app.listen()
Application
app.set(name, value)
将设置项 name
的值设为 value
app.set('title', 'My Site');
app.get('title');
// => "My Site"
app.get(name)
获取设置项 name
的值
app.get('title');
// => undefined
app.set('title', 'My Site');
app.get('title');
// => "My Site"
app.enable(name)
将设置项 name
的值设为 true
.
app.enable('trust proxy');
app.get('trust proxy');
// => true
app.disable(name)
将设置项 name
的值设为 false
.
app.disable('trust proxy');
app.get('trust proxy');
// => false
app.enabled(name)
检查设置项 name
是否已启用
app.enabled('trust proxy');
// => false
app.enable('trust proxy');
app.enabled('trust proxy');
// => true
app.disabled(name)
检查设置项 name
是否已禁用
app.disabled('trust proxy');
// => true
app.enable('trust proxy');
app.disabled('trust proxy');
// => false
app.configure([env], callback)
当 env
和 app.get('env')
(也就是 process.env.NODEENV
) 匹配时, 调用callback
。保留这个方法是出于历史原因,后面列出的if
语句的代码其实更加高效、直接。使用app.set()
配合其它一些配置方法后,没有_必要再使用这个方法。
// 所有环境
app.configure(function(){
app.set('title', 'My Application');
})
// 开发环境
app.configure('development', function(){
app.set('db uri', 'localhost/dev');
})
// 只用于生产环境
app.configure('production', function(){
app.set('db uri', 'n.n.n.n/prod');
})
更高效且直接的代码如下:
// 所有环境
app.set('title', 'My Application');
// 只用于开发环境
if ('development' == app.get('env')) {
app.set('db uri', 'localhost/dev');
}
// 只用于生产环境
if ('production' == app.get('env')) {
app.set('db uri', 'n.n.n.n/prod');
}
app.use([path], function)
使用中间件 function
,可选参数path
默认为"/"。
var express = require('express');
var app = express();
// 一个简单的 logger
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
// 响应
app.use(function(req, res, next){
res.send('Hello World');
});
app.listen(3000);
挂载的路径不会在req里出现,对中间件 function
不可见,这意味着你在function
的回调参数req里找不到path。 这么设计的为了让间件可以在不需要更改代码就在任意"前缀"路径下执行
这里有一个实际应用场景,常见的一个应用是使用./public提供静态文件服务, 用 express.static()
中间件:
// GET /javascripts/jquery.js
// GET /style.css
// GET /favicon.ico
app.use(express.static(__dirname + '/public'));
如果你想把所有的静态文件路径都前缀"/static", 你可以使用“挂载”功能。 如果req.url
不包含这个前缀, 挂载过的中间件不会执行。 当function
被执行的时候,这个参数不会被传递。 这个只会影响这个函数,后面的中间件里得到的 req.url
里将会包含"/static"
// GET /static/javascripts/jquery.js
// GET /static/style.css
// GET /static/favicon.ico
app.use('/static', express.static(__dirname + '/public'));
使用 app.use()
“定义的”中间件的顺序非常重要,它们将会顺序执行,use的先后顺序决定了中间件的优先级。 比如说通常 express.logger()
是最先使用的一个组件,纪录每一个请求
app.use(express.logger());
app.use(express.static(__dirname + '/public'));
app.use(function(req, res){
res.send('Hello');
});
如果你想忽略请求静态文件的纪录,但是对于在 logger()
之后定义的路由和中间件想继续纪录,只需要简单的把 static()
移到前面就行了:
app.use(express.static(__dirname + '/public'));
app.use(express.logger());
app.use(function(req, res){
res.send('Hello');
});
另一个现实的例子,有可能从多个目录提供静态文件服务,下面的例子中会优先从"./public"目录取文件
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/files'));
app.use(express.static(__dirname + '/uploads'));
settings
下面的内建的可以改变Express行为的设置
- env 运行时环境,默认为 process.env.NODE_ENV 或者 "development"
- trust proxy 激活反向代理,默认未激活状态
- jsonp callback name 修改默认?callback=的jsonp回调的名字
- json replacer JSON replacer 替换时的回调, 默认为null
- json spaces JSON 响应的空格数量,开发环境下是2 , 生产环境是0
- case sensitive routing 路由的大小写敏感, 默认是关闭状态, "/Foo" 和"/foo" 是一样的
- strict routing 路由的严格格式, 默认情况下 "/foo" 和 "/foo/" 是被同样对待的
- view cache 模板缓存,在生产环境中是默认开启的
- view engine 模板引擎
- views 模板的目录, 默认是"process.cwd() + ./views"
app.engine(ext, callback)
注册模板引擎的 callback
用来处理ext
扩展名的文件 默认情况下, 根据文件扩展名require()
对应的模板引擎。 比如你想渲染一个 "foo.jade" 文件,Express会在内部执行下面的代码,然后会缓存require()
,这样就可以提高后面操作的性能
app.engine('jade', require('jade').__express);
那些没有提供 .__express
的或者你想渲染一个文件的扩展名与模板引擎默认的不一致的时候,也可以用这个方法。 比如你想用EJS模板引擎来处理 ".html" 后缀的文件:
app.engine('html', require('ejs').renderFile);
这个例子中EJS提供了一个.renderFile()
方法和Express预期的格式: (path, options, callback)
一致, 可以在内部给这个方法取一个别名ejs.__express
,这样你就可以使用".ejs" 扩展而不需要做任何改动
有些模板引擎没有遵循这种转换, 这里有一个小项目consolidate.js 专门把所有的node流行的模板引擎进行了包装,这样它们在Express内部看起来就一样了。
var engines = require('consolidate');
app.engine('haml', engines.haml);
app.engine('html', engines.hogan);
app.param([name], callback)
路由参数的处理逻辑。比如当 :user
出现在一个路由路径中,你也许会自动载入加载用户的逻辑,并把它放置到 req.user
, 或者校验一下输入的参数是否正确。
下面的代码片段展示了callback
很像中间件,但是在参数里多加了一个值,这里名为id
. 它会尝试加载用户信息,然后赋值给req.user
, 否则就传递错误next(err)
.
app.param('user', function(req, res, next, id){
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'));
}
});
});
另外你也可以只传一个callback
, 这样你就有机会改变 app.param()
API. 比如express-params定义了下面的回调,这个允许你使用一个给定的正则去限制参数。
下面的这个例子有一点点高级,检查如果第二个参数是一个正则,返回一个很像上面的"user"参数例子行为的回调函数。
app.param(function(name, fn){
if (fn instanceof RegExp) {
return function(req, res, next, val){
var captures;
if (captures = fn.exec(String(val))) {
req.params[name] = captures;
next();
} else {
next('route');
}
}
}
});
这个函数现在可以非常有效的用来校验参数,或者提供正则捕获后的分组。
app.param('id', /^\d+$/);
app.get('/user/:id', function(req, res){
res.send('user ' + req.params.id);
});
app.param('range', /^(\w+)\.\.(\w+)?$/);
app.get('/range/:range', function(req, res){
var range = req.params.range;
res.send('from ' + range[1] + ' to ' + range[2]);
});
app.VERB(path, [callback…], callback)
app.VERB()
方法为Express提供路由方法, VERB 是指某一个HTTP 动作, 比如 app.post()
。 可以提供多个callbacks,这多个callbacks都将会被平等对待 ,它们的行为跟中间件一样,也有一个例外的情况,如果某一个callback执行了next('route')
,它后面的callback就被忽略。这种情形会应用在当满足一个路由前缀,但是不需要处理这个路由,于是把它向后传递。
下面的代码片段展示最简单的路由定义。Express 会把路径字符串转为正则表达式,然后在符合规则的请求到达时立即使用。 请求参数不会 被考虑进来,比如 "GET /" 会匹配下面的这个路由, 而"GET /?name=tobi"同样也会匹配。
app.get('/', function(req, res){
res.send('hello world');
});
同样也可以使用正则表达式,并且它能够在你指定特定路径的时候发挥大作用。 比如下面的例子可以匹配"GET /commits/71dbb9c" , 同时也能匹配 "GET /commits/71dbb9c..4c084f9".
app.get(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, function(req, res){
var from = req.params[0];
var to = req.params[1] || 'HEAD';
res.send('commit range ' + from + '..' + to);
});
可以传递一些回调,这对复用一些加载资源、校验的中间件很有用。
app.get('/user/:id', user.load, function(){
// ...
})
这些回调同样可以通过数组传递,简单的放置在数组中即可。
var middleware = [loadForum, loadThread];
app.get('/forum/:fid/thread/:tid', middleware, function(){
// ...
})
app.post('/forum/:fid/thread/:tid', middleware, function(){
// ...
})
app.all(path, [callback…], callback)
这个方法很像app.VERB()
, 但是它匹配所有的HTTP动作
这个方法在给特定前缀路径或者任意路径上处理时会特别有用。 比如你想把下面的路由放在所有其它路由之前,它需要所有从这个路由开始的加载验证,并且自动加载一个用户 记住所有的回调都不应该被当作终点, loadUser
能够被当作一个任务,然后next()
去匹配接下来的路由。
app.all('*', requireAuthentication, loadUser);
Or the equivalent:
app.all('*', requireAuthentication)
app.all('*', loadUser);
另一个非常赞的例子是全局白名单函数。这里有一个例子跟前一个很像,但是它限制前缀为"/api":
app.all('/api/*', requireAuthentication);
app.locals
应用程序本地变量会附加给所有的在这个应用程序内渲染的模板。 这是一个非常有用的模板函数,就像应用程序级数据一样。
app.locals.title = 'My App';
app.locals.strftime = require('strftime');
app.locals
对象是一个JavaScript Function
, 执行的时候它会把属性合并到它自身,提供了一种简单展示已有对象作为本地变量的方法
app.locals({
title: 'My App',
phone: '1-250-858-9990',
email: 'me@myapp.com'
});
app.locals.title
// => 'My App'
app.locals.email
// => 'me@myapp.com'
app.locals
对象最终会是一个JavaScript函数对象,你不可以使用Functions和Objects内置的属性,比如name, apply, bind, call, arguments, length, constructor
app.locals({name: 'My App'});
app.locals.name
// => 返回 'app.locals' 而不是 'My App' (app.locals 是一个函数 !)
// => 如果name变量用在一个模板里,发返回一个 ReferenceError
全部的保留字列表可以在很多规范里找到。 JavaScript 规范 介绍了原来的属性,有一些还会被现代的JS引擎识别,EcmaScript 规范 在它的基础上,统一了值,添加了一些,删除了一些废弃的。如果感兴趣,可以看看Functions和Objects的属性值。
默认情况下Express只有一个应用程序级本地变量,它是 settings
.
app.set('title', 'My App');
// 在view里使用 settings.title
app.render(view, [options], callback)
渲染 view
, callback
用来处理返回的渲染后的字符串。 这个是 res.render()
的应用程序级版本,它们的行为是一样的。
app.render('email', function(err, html){
// ...
});
app.render('email', { name: 'Tobi' }, function(err, html){
// ...
});
app.routes
app.routes
对象存储了所有的被HTTP verb定义路由。 这个对象可以用在一些内部功能上,比如Express不仅用它来做路由分发,同时在没有app.options()
定义的情况下用它来处理默认的<string>OPTIONS</string>行为。 你的应用程序或者框架也可以很轻松的通过在这个对象里移除路由来达到删除路由的目的。
console.log(app.routes)
{ get:
[ { path: '/',
method: 'get',
callbacks: [Object],
keys: [],
regexp: /^\/\/?$/i },
{ path: '/user/:id',
method: 'get',
callbacks: [Object],
keys: [{ name: 'id', optional: false }],
regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ],
delete:
[ { path: '/user/:id',
method: 'delete',
callbacks: [Object],
keys: [Object],
regexp: /^\/user\/(?:([^\/]+?))\/?$/i } ] }
app.listen()
在给定的主机和端口上监听请求,这个和node的文档http.Server#listen()是一致的
var express = require('express');
var app = express();
app.listen(3000);
express()
返回的app
实际上是一个JavaScriptFunction
,它被设计为传给node的http servers作为处理请求的回调函数。因为app
不是从HTTP或者HTTPS继承来的,它只是一个简单的回调函数,你可以以同一份代码同时处理HTTP and 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()
方法只是一个快捷方法,如果你想使用HTTPS,或者同时提供HTTP和HTTPS,可以使用上面的代码
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};