应用对象
app 对象通常表示 Express 应用。通过调用 Express 模块导出的顶层 express() 函数来创建它:
🌐 The app object conventionally denotes the Express application.
Create it by calling the top-level express() function exported by the Express module:
var express = require('express');var app = express();
app.get('/', function (req, res) { res.send('hello world');});
app.listen(3000);属性
🌐 Properties
app.locals
应用本地变量会提供给应用中渲染的所有模板。这对于向模板提供辅助函数以及应用级别的数据非常有用。
🌐 Application local variables are provided to all templates rendered within the application. This is useful for providing helper functions to templates, as well as app-level data.
app.locals.title = 'My App';app.locals.strftime = require('strftime');app.locals 对象是一个 JavaScript Function,当使用一个对象调用它时,它会将属性合并到自身,从而提供一种将现有对象作为本地变量公开的简单方法。
🌐 The app.locals object is a JavaScript Function,
which when invoked with an object will merge properties into itself, providing
a simple way to expose existing objects as local variables.
app.locals({ title: 'My App', phone: '1-250-858-9990', email: 'me@myapp.com',});
console.log(app.locals.title);// => 'My App'
console.log(app.locals.email);// => 'me@myapp.com'app.locals 对象最终是一个 Javascript 函数对象,其结果是你不能将已有的(原生的)命名属性用于你自己的变量名,例如 name, apply, bind, call, arguments, length, constructor。
🌐 A consequence of the app.locals Object being ultimately a Javascript Function Object is that you must not reuse existing (native) named properties for your own variable names, such as name, apply, bind, call, arguments, length, constructor.
app.locals({ name: 'My App' });
console.log(app.locals.name);// => return 'app.locals' in place of 'My App' (app.locals is a Function !)// => if name's variable is used in a template, a ReferenceError will be returned.本地命名属性的完整列表可以在许多规范中找到。 JavaScript 规范 引入了原始属性,其中一些仍被现代引擎识别,随后 EcmaScript 规范 在此基础上进行了扩展并规范了属性集合,添加了新的属性并移除了已弃用的属性。如果感兴趣,可以查看函数和对象的属性。
默认情况下,Express 仅公开一个应用级本地变量 settings。
🌐 By default Express exposes only a single app-level local variable, settings.
app.set('title', 'My App');// use settings.title in a viewapp.routes
app.routes 对象包含所有定义的路由,并按相关的 HTTP 动词进行映射。该对象可用于自省功能,例如 Express 内部不仅用于路由,还用于提供默认功能。
🌐 The app.routes object houses all of the routes defined mapped
by the associated HTTP verb. This object may be used for introspection capabilities,
for example Express uses this internally not only for routing but to provide default
app.options()。你的应用或框架也可以通过仅从此对象中移除它们来删除路由。
console.log(app.routes) 的输出:
🌐 The output of 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 } ] }settings
提供以下设置以更改 Express 的行为方式:
🌐 The following settings are provided to alter how Express will behave:
env环境模式,默认为 process.env.NODE_ENV 或 “development”trust proxy启用反向代理支持,默认情况下禁用jsonp callback name更改 ?callback= 的默认回调名称json replacerJSON 替换器回调,默认值为 nulljson spacesJSON 响应的格式化空格,开发环境默认为 2,生产环境默认为 0case sensitive routing启用区分大小写,默认情况下禁用,将“/Foo”和“/foo”视为相同strict routing启用严格路由,默认情况下路由会将 “/foo” 和 “/foo/” 视为相同view cache启用视图模板编译缓存,默认在生产环境中启用view engine默认使用的引擎扩展,当省略时使用views视图目录路径,默认值为 “process.cwd() + ‘/views‘“
方法
🌐 Methods
app.set(名称, 值)
🌐 app.set(name, value)
将设置 name 分配为 value。
🌐 Assigns setting name to value.
app.set('title', 'My Site');app.get('title');// => "My Site"app.get(name)
获取设置 name 的值。
🌐 Get setting name value.
app.get('title');// => undefined
app.set('title', 'My Site');app.get('title');// => "My Site"app.enable(name)
将设置 name 设置为 true。
🌐 Set setting name to true.
app.enable('trust proxy');app.get('trust proxy');// => trueapp.disable(name)
将设置 name 设置为 false。
🌐 Set setting name to false.
app.disable('trust proxy');app.get('trust proxy');// => falseapp.enabled(name)
检查设置 name 是否已启用。
🌐 Check if setting name is enabled.
app.enabled('trust proxy');// => false
app.enable('trust proxy');app.enabled('trust proxy');// => trueapp.disabled(name)
检查设置 name 是否被禁用。
🌐 Check if setting name is disabled.
app.disabled('trust proxy');// => true
app.enable('trust proxy');app.disabled('trust proxy');// => falseapp.configure([env], 回调)
🌐 app.configure([env], callback)
当 env 匹配 app.get('env') 时,有条件地调用 callback,也称为 process.env.NODE_ENV。该方法保留是出于兼容性原因,实际上是一个 if 语句,如以下代码片段所示。这些函数不是使用 app.set() 和其他配置方法所必需的。
// all environmentsapp.configure(function () { app.set('title', 'My Application');});
// development onlyapp.configure('development', function () { app.set('db uri', 'localhost/dev');});
// production onlyapp.configure('production', function () { app.set('db uri', 'n.n.n.n/prod');});实际上是糖对于:
🌐 Is effectively sugar for:
// all environmentsapp.set('title', 'My Application');
// development onlyif (app.get('env') === 'development') { app.set('db uri', 'localhost/dev');}
// production onlyif (app.get('env') === 'production') { app.set('db uri', 'n.n.n.n/prod');}app.use([路径], 函数)
🌐 app.use([path], function)
使用指定的中间件 function,可选挂载点为 path,默认为 ”/”。
🌐 Use the given middleware function, with optional mount path,
defaulting to ”/”.
var express = require('express');var app = express();
// simple loggerapp.use(function (req, res, next) { console.log('%s %s', req.method, req.url); next();});
// respondapp.use(function (req, res, next) { res.send('Hello World');});
app.listen(3000);“mount”路径被移除,并且对中间件 function 不可 见。该功能的主要作用是,挂载的中间件无论其“前缀”路径名如何,都可以在不修改代码的情况下运行。
Caution
一个路由将匹配任何紧跟其路径的路径,其后接 / 或 .。例如:app.use('/apple', ...) 将匹配 /apple、/apple/images、/apple/images/news、/apple.html、/apple.html.txt 等等。
🌐 A route will match any path that follows its path immediately with either a “/” or a “.”. For example: app.use('/apple', ...) will match /apple, /apple/images, /apple/images/news, /apple.html, /apple.html.txt, and so on.
这里有一个具体的例子,以在 ./public 中提供文件的典型用例为例,使用 express.static() 中间件:
🌐 Here’s a concrete example, take the typical use-case of serving files in ./public
using the express.static() middleware:
// GET /javascripts/jquery.js// GET /style.css// GET /favicon.icoapp.use(express.static(path.join(__dirname, 'public')));例如,假设你想给所有静态文件加上 “/static” 前缀,你可以使用“挂载”功能来实现这一点。挂载的中间件函数不会被调用,除非 req.url 包含该前缀,此时在函数被调用时该前缀会被去掉。这只影响该函数,后续中间件仍会看到包含 “/static” 的 req.url,除非它们也被挂载。
🌐 Say for example you wanted to prefix all static files with “/static”, you could
use the “mounting” feature to support this. Mounted middleware functions are not
invoked unless the req.url contains this prefix, at which point
it is stripped when the function is invoked. This affects this function only,
subsequent middleware will see req.url with “/static” included
unless they are mounted as well.
// GET /static/javascripts/jquery.js// GET /static/style.css// GET /static/favicon.icoapp.use('/static', express.static(path.join(__dirname, 'public')));使用 app.use() “定义” 的中间件顺序非常重要,它们会按顺序调用,因此这决定了中间件的优先级。例如,通常 express.logger() 是你会使用的第一个中间件,用于记录每个请求:
🌐 The order of which middleware are “defined” using app.use() is
very important, they are invoked sequentially, thus this defines middleware
precedence. For example usually express.logger() is the very
first middleware you would use, logging every request:
app.use(express.logger());app.use(express.static(path.join(__dirname, 'public')));app.use(function (req, res) { res.send('Hello');});现在假设你想忽略对静态文件的日志记录请求,但要继续记录在 logger() 之后定义的路由和中间件,你只需将 static() 上移即可:
🌐 Now suppose you wanted to ignore logging requests for static files, but to
continue logging routes and middleware defined after logger(),
you would simply move static() above:
app.use(express.static(path.join(__dirname, 'public')));app.use(express.logger());app.use(function (req, res) { res.send('Hello');});另一个具体的例子是从多个目录提供文件,优先使用 ”./public” 而不是其他目录:
🌐 Another concrete example would be serving files from multiple directories, giving precedence to ”./public” over the others:
app.use(express.static(path.join(__dirname, 'public')));app.use(express.static(path.join(__dirname, 'files')));app.use(express.static(path.join(__dirname, 'uploads')));app.engine(ext, 回调)
🌐 app.engine(ext, callback)
将给定的模板引擎 callback 注册为 ext
🌐 Register the given template engine callback as ext
默认情况下将根据文件扩展名使用 require() 引擎。例如,如果你尝试渲染一个 “foo.jade” 文件,Express 将在内部调用以下内容,并在后续调用中缓存 require() 以提高性能。
🌐 By default will require() the engine based on the
file extension. For example if you try to render
a “foo.jade” file Express will invoke the following internally,
and cache the require() on subsequent calls to increase
performance.
app.engine('jade', require('jade').__express);对于那些默认不提供 .__express 的引擎——或者如果你希望将不同的扩展名“映射”到模板引擎,可以使用此方法。例如,将 EJS 模板引擎映射到 “.html” 文件:
🌐 For engines that do not provide .__express out of the box -
or if you wish to “map” a different extension to the template engine
you may use this method. For example mapping the EJS template engine to
“.html” files:
app.engine('html', require('ejs').renderFile);在这种情况下,EJS 提供了一个 .renderFile() 方法,其签名与 Express 期望的相同:(path, options, callback),不过请注意,它在内部将此方法别名为 ejs.__express,所以如果你使用 “.ejs” 扩展名,你不需要做任何事情。
🌐 In this case EJS provides a .renderFile() method with
the same signature that Express expects: (path, options, callback),
though note that it aliases this method as ejs.__express internally
so if you’re using “.ejs” extensions you don’t need to do anything.
有些模板引擎不遵循这个惯例,
🌐 Some template engines do not follow this convention, the
consolidate.js 库的创建是为了将所有流行的 Node 模板引擎映射到这个约定,从而允许它们在 Express 中无缝工作。
var engines = require('consolidate');app.engine('haml', engines.haml);app.engine('html', engines.hogan);app.param([name], 回调)
🌐 app.param([name], callback)
将逻辑映射到路由参数。例如,当路由路径中存在 :user 时,你可以将用户加载逻辑映射到自动为路由提供 req.user,或者对参数输入执行验证。
🌐 Map logic to route parameters. For example when :user
is present in a route path you may map user loading logic to automatically
provide req.user to the route, or perform validations
on the parameter input.
以下代码片段演示了 callback 是多么类似于中间件,因此支持异步操作,然而提供了额外的参数值,这里命名为 id。然后尝试加载用户,将结果赋给 req.user,否则将错误传递给 next(err)。
🌐 The following snippet illustrates how the callback
is much like middleware, thus supporting async operations, however
providing the additional value of the parameter, here named as id.
An attempt to load the user is then performed, assigning req.user,
otherwise passing an error to 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”参数示例。
🌐 This example is a bit more advanced, checking if the second argument is a regular expression, returning the callback which acts much like the “user” param example.
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'); } }; }});该方法现在可以用来有效地验证参数,也可以解析它们以提供捕获组:
🌐 The method could now be used to effectively validate parameters, or also parse them to provide capture groups:
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(路径, [回调…], 回调)
🌐 app.VERB(path, [callback…], callback)
app.VERB() 方法在 Express 中提供路由功能,其中 VERB 是 HTTP 动词之一,例如 app.post()。可以提供多个回调函数,所有回调函数都被同等对待,行为就像中间件一样,唯一的例外是这些回调函数可以调用 next('route') 来跳过剩余的路由回调函数。该机制可用于在路由上执行前置条件,然后在没有理由继续匹配的路由时将控制权传递给后续路由。
以下代码片段展示了可能的最简单的路由定义。Express 将路径字符串转换为正则表达式,内部用于匹配传入的请求。在执行这些匹配时,不会考虑查询字符串,例如 “GET /” 将匹配以下路由,同样 “GET /?name=tobi” 也会匹配。
app.get('/', function (req, res) { res.send('hello world');});也可以使用正则表达式,如果你有非常具体的限制,它可能会很有用,例如以下内容可以匹配 “GET /commits/71dbb9c” 以及 “GET /commits/71dbb9c..4c084f9”。
🌐 Regular expressions may also be used, and can be useful if you have very specific restraints, for example the following would match “GET /commits/71dbb9c” as well as “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);});还可以传递多个回调,这对于重复使用加载资源、执行验证等的中间件非常有用。
🌐 Several callbacks may also be passed, useful for re-using middleware that load resources, perform validations, etc.
app.get('/user/:id', user.load, function () { // ...});这些回调也可以以数组形式传入,这些数组在传入时会被简单展开:
🌐 These callbacks may be passed within arrays as well, these arrays are simply flattened when passed:
var middleware = [loadForum, loadThread];
app.get('/forum/:fid/thread/:tid', middleware, function () { // ...});
app.post('/forum/:fid/thread/:tid', middleware, function () { // ...});app.all(path, [回调…], 回调)
🌐 app.all(path, [callback…], callback)
此方法的功能与 app.VERB() 方法完全相同,然而它匹配所有 HTTP 动词。
🌐 This method functions just like the app.VERB() methods,
however it matches all HTTP verbs.
这种方法对于为特定路径前缀或任意匹配映射“全局”逻辑非常有用。例如,如果你将以下路由放在所有其他路由定义的顶部,那么从此点开始的所有路由都将需要认证,并自动加载用户。请记住,这些回调不必作为终点,loadUser 可以执行一个任务,然后 next() 继续匹配后续路由。
🌐 This method is extremely useful for
mapping “global” logic for specific path prefixes or arbitrary matches.
For example if you placed the following route at the top of all other
route definitions, it would require that all routes from that point on
would require authentication, and automatically load a user. Keep in mind
that these callbacks do not have to act as end points, loadUser
can perform a task, then next() to continue matching subsequent
routes.
app.all('*', requireAuthentication, loadUser);或者等同于:
🌐 Or the equivalent:
app.all('*', requireAuthentication);app.all('*', loadUser);另一个很好的例子是白名单的“全局”功能。这里的例子与之前类似,但只限制以“/api”开头的路径:
🌐 Another great example of this is white-listed “global” functionality. Here the example is much like before, however only restricting paths prefixed with “/api”:
app.all('/api/*', requireAuthentication);app.render(view, [options], 回调)
🌐 app.render(view, [options], callback)
使用回调渲染一个 view,回调会返回渲染后的字符串。这是 res.render() 的应用级变体,其他方面的行为相同。
🌐 Render a view with a callback responding with
the rendered string. This is the app-level variant of res.render(),
and otherwise behaves the same way.
app.render('email', function (err, html) { // ...});
app.render('email', { name: 'Tobi' }, function (err, html) { // ...});app.listen()
在指定的主机和端口上绑定并监听连接,这个方法与 Node 的 http.Server#listen()完全相同。
var express = require('express');var app = express();app.listen(3000);express() 返回的 app 实际上是一个 JavaScript Function,旨在作为回调传递给 node 的 http 服务器以处理请求。这使你可以轻松地使用相同的代码库提供应用的 HTTP 和 HTTPS 版本,因为应用并没有继承自这些,它只是一个回调:
🌐 The app returned by express() is in fact a JavaScript
Function, designed to be passed to node’s http servers as a callback
to handle requests. This allows you to provide both HTTP and HTTPS versions of
your app with the same codebase easily, as the app does not inherit from these,
it is simply a callback:
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 或提供两者,请使用上述技巧。
🌐 The app.listen() method is simply a convenience method defined as,
if you wish to use HTTPS or provide both, use the technique above.
app.listen = function () { var server = http.createServer(this); return server.listen.apply(server, arguments);};