🌐 Nodejs.cn

应用对象

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:

index.cjs
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('hello world');
});
app.listen(3000);

app 对象有以下方法

🌐 The app object has methods for

它还有一些设置(属性),会影响应用的行为; 有关更多信息,请参阅 应用设置

🌐 It also has settings (properties) that affect how the application behaves; for more information, see Application Settings.

Note

Express 应用对象可以分别从 请求对象响应对象 中引用,分别为 req.appres.app

🌐 The Express application object can be referred from the request object and the response object as req.app, and res.app, respectively.

属性

🌐 Properties

app.locals

app.locals 对象具有作为应用本地变量的属性,并且将在使用 res.render 渲染的模板中可用。

🌐 The app.locals object has properties that are local variables within the application, and will be available in templates rendered with res.render.

Warning

locals 对象被视图引擎用于渲染响应。对象的键可能特别敏感,不应包含用户控制的输入,因为这可能影响视图引擎的操作或提供跨站脚本攻击的途径。有关更多注意事项,请查阅所使用视图引擎的文档。

🌐 The locals object is used by view engines to render a response. The object keys may be particularly sensitive and should not contain user-controlled input, as it may affect the operation of the view engine or provide a path to cross-site scripting. Consult the documentation for the used view engine for additional considerations.

console.dir(app.locals.title);
// => 'My App'
console.dir(app.locals.email);
// => 'me@myapp.com'

一旦设置,app.locals 属性的值将在整个应用的生命周期内持续存在, 与 res.locals 属性不同,后者仅在请求的生命周期内有效。

🌐 Once set, the value of app.locals properties persist throughout the life of the application, in contrast with res.locals properties that are valid only for the lifetime of the request.

你可以访问在应用中渲染的模板中的局部变量。这对于向模板提供辅助函数以及应用级数据非常有用。通过 req.app.locals,局部变量在中间件中是可用的(参见 req.app

🌐 You can access local variables in templates rendered within the application. This is useful for providing helper functions to templates, as well as application-level data. Local variables are available in middleware via req.app.locals (see req.app)

app.locals.title = 'My App';
app.locals.strftime = require('strftime');
app.locals.email = 'me@myapp.com';

app.mountpath

app.mountpath 属性包含一个或多个子应用挂载的路径模式。

🌐 The app.mountpath property contains one or more path patterns on which a sub-app was mounted.

Note

子应用是 express 的一个实例,可用于处理对某个路由的请求。

🌐 A sub-app is an instance of express that may be used for handling the request to a route.

index.cjs
var express = require('express');
var app = express(); // the main app
var admin = express(); // the sub app
admin.get('/', function (req, res) {
console.log(admin.mountpath); // /admin
res.send('Admin Homepage');
});
app.use('/admin', admin); // mount the sub app

它类似于 req 对象的 baseUrl 属性,不同之处在于 req.baseUrl 返回匹配的 URL 路径,而不是匹配的模式。

🌐 It is similar to the baseUrl property of the req object, except req.baseUrl returns the matched URL path, instead of the matched patterns.

如果一个子应用挂载在多个路径模式上,app.mountpath 会返回它挂载的模式列表,如下例所示。

🌐 If a sub-app is mounted on multiple path patterns, app.mountpath returns the list of patterns it is mounted on, as shown in the following example.

var admin = express();
admin.get('/', function (req, res) {
console.dir(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); // load the 'secret' router on '/secr*t', on the 'admin' sub app
app.use(['/adm*n', '/manager'], admin); // load the 'admin' router on '/adm*n' and '/manager', on the parent app

事件

🌐 Events

app.on(‘mount’)

Arguments

callback
Type:Function

mount 事件触发时,监听器会被调用。它的唯一参数是父应用:callback(parent)

mount 事件在子应用挂载到父应用上时触发。父应用会被传递给回调函数。

🌐 The mount event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function.

Note

子应用将会:

🌐 Sub-apps will:

  • 不要继承具有默认值的设置。你必须在子应用中设置该值。
  • 继承没有默认值的设置的值。

有关详细信息,请参见 应用设置

🌐 For details, see Application settings.

var admin = express();
admin.on('mount', function (parent) {
console.log('Admin Mounted');
console.log(parent); // refers to the parent app
});
admin.get('/', function (req, res) {
res.send('Admin Homepage');
});
app.use('/admin', admin);

方法

🌐 Methods

app.all()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

此方法类似于标准的 app.METHOD() 方法,除了它匹配所有 HTTP 动词。

🌐 This method is like the standard app.METHOD() methods, except it matches all HTTP verbs.

例子

🌐 Examples

以下回调会针对对 /secret 的请求执行,无论是使用 GET、POST、PUT、DELETE 还是任何其他 HTTP 请求方法:

🌐 The following callback is executed for requests to /secret whether using GET, POST, PUT, DELETE, or any other HTTP request method:

app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...');
next(); // pass control to the next handler
});

app.all() 方法对于为特定路径前缀或任意匹配映射“全局”逻辑非常有用。例如,如果你将以下内容放在所有其他路由定义的顶部,它将要求从该点开始的所有路由都需要身份验证,并自动加载用户。请记住,这些回调不必作为终点:loadUser 可以执行某个任务,然后调用 next() 来继续匹配后续路由。

🌐 The app.all() method is useful for mapping “global” logic for specific path prefixes or arbitrary matches. For example, if you put the following at the top of all other route definitions, it requires that all routes from that point on 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 call next() to continue matching subsequent routes.

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

或者等同于:

🌐 Or the equivalent:

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

另一个例子是白名单“全局”功能。这个例子类似于上面的例子,但它只限制以“/api”开头的路径:

🌐 Another example is white-listed “global” functionality. The example is similar to the ones above, but it only restricts paths that start with “/api”:

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

app.delete()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

将 HTTP DELETE 请求路由到指定路径,并使用指定的回调函数。 更多信息,请参见 路由指南

🌐 Routes HTTP DELETE requests to the specified path with the specified callback functions. For more information, see the routing guide.

示例

🌐 Example

app.delete('/', function (req, res) {
res.send('DELETE request to homepage');
});

app.del()

Arguments

path
Type:String | RegExp | Array

调用中间件函数的路径;与 app.delete() 相同。

callback
Type:Function | Function[]

回调函数;与 app.delete() 相同。

Caution

app.del()app.delete() 的别名,最初引入的原因是 delete 在较旧的 JavaScript 引擎中是保留字。从 Express v4.2.0 开始,它会发出弃用警告。请改用 app.delete()

你可以使用以下代码转换自动更新你的代码:

🌐 You can update your code automatically with the following codemod:

Terminal window
npx codemod@latest @expressjs/route-del-to-delete

app.disable()

Arguments

name
Type:String

要禁用的布尔设置的名称;来自应用设置表的属性之一。

将布尔设置 name 设置为 false,其中 name应用设置表 中的某个属性。调用布尔属性的 app.set('foo', false) 与调用 app.disable('foo') 相同。

🌐 Sets the Boolean setting name to false, where name is one of the properties from the app settings table. Calling app.set('foo', false) for a Boolean property is the same as calling app.disable('foo').

例如:

🌐 For example:

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

app.disabled()

Arguments

name
Type:String

要测试的布尔设置的名称;来自应用设置表的属性之一。

如果布尔设置 name 被禁用(false),则返回 true,其中 name应用设置表 中的某个属性。

🌐 Returns true if the Boolean setting name is disabled (false), where name is one of the properties from the app settings table.

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

app.enable()

Arguments

name
Type:String

要启用的布尔设置的名称;来自应用设置表的属性之一。

将布尔设置 name 设置为 true,其中 name应用设置表 中的某个属性。调用布尔属性的 app.set('foo', true) 与调用 app.enable('foo') 相同。

🌐 Sets the Boolean setting name to true, where name is one of the properties from the app settings table. Calling app.set('foo', true) for a Boolean property is the same as calling app.enable('foo').

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

app.enabled()

Arguments

name
Type:String

要测试的设置名称;来自应用设置表的属性之一。

如果设置 name 被启用(true),则返回 true,其中 name应用设置表 中的属性之一。

🌐 Returns true if the setting name is enabled (true), where name is one of the properties from the app settings table.

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

app.engine()

Arguments

ext
Type:String

模板引擎注册的文件扩展名(例如,pughtml)。

callback
Type:Function

用于渲染具有给定扩展名文件的模板引擎函数。

将给定的模板引擎 callback 注册为 ext

🌐 Registers the given template engine callback as ext.

默认情况下,Express 会根据文件扩展名 require() 引擎。例如,如果你尝试渲染一个 “foo.pug” 文件,Express 会在内部调用以下内容,并在随后的调用中缓存 require() 以提高性能。

🌐 By default, Express will require() the engine based on the file extension. For example, if you try to render a “foo.pug” file, Express invokes the following internally, and caches the require() on subsequent calls to increase performance.

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

对于不提供 .__express 的引擎,或者如果你希望将不同的扩展名“映射”到模板引擎,请使用此方法。

🌐 Use this method for engines that do not provide .__express out of the box, or if you wish to “map” a different extension to the template engine.

例如,将 EJS 模板引擎映射到 “.html” 文件:

🌐 For example, to map 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.

有些模板引擎不遵循此约定。consolidate.js 库将 Node 模板引擎映射为遵循此约定,因此它们可以与 Express 无缝协作。

🌐 Some template engines do not follow this convention. The consolidate.js library maps Node template engines to follow this convention, so they work seamlessly with Express.

index.cjs
var engines = require('consolidate');
app.engine('haml', engines.haml);
app.engine('html', engines.hogan);

app.get(name)

Arguments

name
Type:String

要读取的设置名称;在应用设置表中的字符串之一。

返回 name 应用设置的值,其中 name应用设置表 中的字符串之一。例如:

🌐 Returns the value of name app setting, where name is one of the strings in the app settings table. For example:

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

app.get()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

将 HTTP GET 请求路由到指定路径,并使用指定的回调函数。

🌐 Routes HTTP GET requests to the specified path with the specified callback functions.

有关更多信息,请参阅路由指南

🌐 For more information, see the routing guide.

示例

🌐 Example

app.get('/', function (req, res) {
res.send('GET request to homepage');
});

app.listen()

Arguments

port
Type:Number | undefined

要监听的 TCP 端口。如果省略或为 0,操作系统会分配一个任意未使用的端口。

host
Type:String | undefined

接受连接的主机。

backlog
Type:Number | undefined

等待连接队列的最大长度。

path
Type:String | undefined

要监听的 UNIX 套接字路径,可作为主机和端口的替代。

callback
Type:Function | undefined

服务器开始监听后要调用的函数。

在指定的主机和端口上绑定并监听连接,或启动 UNIX 套接字并在给定路径上监听连接。此方法与 Node 的 http.Server.listen() 相同。

🌐 Binds and listens for connections on the specified host and port, or starts a UNIX socket and listens for connections on the given path. This method is identical to Node’s http.Server.listen().

如果省略端口或端口为0,操作系统将分配一个任意未使用的端口,这对于自动化任务(测试等)非常有用。

🌐 If port is omitted or is 0, the operating system will assign an arbitrary unused port, which is useful for cases like automated tasks (tests, etc.).

index.cjs
var express = require('express');
var app = express();
app.listen(3000); // bind and listen on a TCP port
app.listen('/tmp/sock'); // or listen on a UNIX socket

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 makes it easy to provide both HTTP and HTTPS versions of your app with the same code base, as the app does not inherit from these (it is simply a callback):

index.cjs
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 对象,并且(对于 HTTP)是以下操作的便利方法:

🌐 The app.listen() method returns an http.Server object and (for HTTP) is a convenience method for the following:

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

Note

Node 的 http.Server.listen() 方法的所有形式实际上都是受支持的。

🌐 All the forms of Node’s http.Server.listen() method are in fact actually supported.

app.METHOD()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

路由一个 HTTP 请求,其中 METHOD 是请求的 HTTP 方法,例如 GET、PUT、POST 等,使用小写。因此,实际的方法是 app.get()app.post()app.put() 等。完整列表请参见下面的 路由方法

🌐 Routes an HTTP request, where METHOD is the HTTP method of the request, such as GET, PUT, POST, and so on, in lowercase. Thus, the actual methods are app.get(), app.post(), app.put(), and so on. See Routing methods below for the complete list.

路由方法

🌐 Routing methods

Express 支持与同名 HTTP 方法对应的以下路由方法:

🌐 Express supports the following routing methods corresponding to the HTTP methods of the same names:

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

API 文档只对最常用的 HTTP 方法 app.get()app.post()app.put()app.delete()app.query() 有明确条目。然而,上面列出的其他方法的工作方式完全相同。

🌐 The API documentation has explicit entries only for the most popular HTTP methods app.get(), app.post(), app.put(), app.delete(), and app.query(). However, the other methods listed above work in exactly the same way.

对于映射到无效 JavaScript 变量名的方法,请使用括号表示法。例如,app['m-search']('/', function ...

🌐 To route methods that translate to invalid JavaScript variable names, use the bracket notation. For example, app['m-search']('/', function ....

app.get('/', function (req, res) {
res.send('GET request to homepage');
});
app.post('/', function (req, res) {
res.send('POST request to homepage');
});
// methods that are invalid JavaScript variable names use bracket notation
app['m-search']('/', function (req, res) {
res.send('M-SEARCH request to homepage');
});

Note

app.get() 函数会自动被调用用于 HTTP HEAD 方法,此外,如果 app.head()app.get() 之前没有被调用过该路径,也会用于 GET 方法。

🌐 The app.get() function is automatically called for the HTTP HEAD method in addition to the GET method if app.head() was not called for the path before app.get().

方法 app.all() 并没有派生自任何 HTTP 方法,它会在指定路径加载用于 所有 HTTP 请求方法的中间件。有关更多信息,请参见 app.all

🌐 The method, app.all(), is not derived from any HTTP method and loads middleware at the specified path for all HTTP request methods. For more information, see app.all.

有关路由的更多信息,请参阅routing guide

🌐 For more information on routing, see the routing guide.

app.query()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

将 HTTP QUERY 请求路由到指定路径,并使用指定的回调函数。 更多信息,请参见 路由指南

🌐 Routes HTTP QUERY requests to the specified path with the specified callback functions. For more information, see the routing guide.

Caution

HTTP QUERY 方法于 2026 年 6 月 15 日被标准化为 RFC 10008。客户端和中间件的支持仍然有限。在生产环境中依赖 QUERY 方法之前,请确认你的目标环境是否支持该动词。

🌐 The HTTP QUERY method was standardized on June 15, 2026 as RFC 10008. Client and intermediary support is still limited. Verify that your target environments support the QUERY verb before relying on it in production.

HTTP QUERY 方法 (RFC 10008) 旨在处理那些输入过大或过于敏感而无法编码到 URL 中的查询。与 GET 不同,QUERY 请求携带包含查询输入的主体。与 POST 不同,它明确 是安全且幂等的,中间件和缓存可以自动重试,而且响应是可缓存的(缓存键中包含请求体)。

🌐 The HTTP QUERY method (RFC 10008) is designed for queries whose input is too large or too sensitive to encode in the URL. Unlike GET, a QUERY request carries a body containing the query input. Unlike POST, it is explicitly safe and idempotent, intermediaries and caches can retry it automatically, and the response is cacheable (with the request body included in the cache key).

当查询参数可能超过 URL 长度限制、包含不应出现在服务器日志或浏览器历史记录中的敏感数据,或者太复杂而无法清晰地表达为 URL 查询字符串时(例如深度嵌套的过滤器或结构化条件),请使用 app.query()

🌐 Use app.query() when query parameters would exceed URL length limits, contain sensitive data that should not appear in server logs or browser history, or are too complex to express cleanly as a URL query string, such as deeply nested filters or structured criteria.

因为查询输入在请求正文中传递,req.bodyundefined,直到你挂载一个解析正文的中间件,比如 express.json()express.urlencoded(),它能够匹配请求的 Content-Type

🌐 Because the query input travels in the request body, req.body is undefined until you mount a body-parsing middleware such as express.json() or express.urlencoded() that matches the request’s Content-Type.

示例

🌐 Example

index.js
app.use(express.json()); // populate req.body for application/json payloads
app.query('/search', (req, res) => {
// complex filter criteria arrive in req.body, not the URL
const results = db.search(req.body);
res.json(results);
});

app.param()

Arguments

name
Type:String | String[] | undefined

路由参数的名称,或名称数组。

callback
Type:Function

被作为 callback(req, res, next, value, name) 调用的触发器:请求对象、响应对象、下一个中间件、参数的值以及参数的名称,按此顺序。

路由参数 添加回调触发器,其中 name 是参数的名称或它们的数组,callback 是回调函数。回调函数的参数依次为请求对象、响应对象、下一个中间件、参数的值和参数的名称。

🌐 Add callback triggers to route parameters, where name is the name of the parameter or an array of them, and callback is the callback function. The parameters of the callback function are the request object, the response object, the next middleware, the value of the parameter and the name of the parameter, in that order.

如果 name 是一个数组,那么 callback 触发器会为其中声明的每个参数按声明顺序注册。此外,对于每个已声明的参数(除了最后一个),在回调中调用 next 会调用下一个已声明参数的回调。对于最后一个参数,调用 next 会调用当前正在处理的路由中下一个中间件,就像 name 只是一个字符串时一样。

🌐 If name is an array, the callback trigger is registered for each parameter declared in it, in the order in which they are declared. Furthermore, for each declared parameter except the last one, a call to next inside the callback will call the callback for the next declared parameter. For the last parameter, a call to next will call the next middleware in place for the route currently being processed, just like it would if name were just a string.

例如,当 :user 出现在路由路径中时,你可以将用户加载逻辑映射,以自动向路由提供 req.user,或对参数输入执行验证。

🌐 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.

app.param('user', function (req, res, next, id) {
// try to get the user details from the User model and attach it to the request object
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 上定义的参数回调函数只会被 app 路由上定义的路由参数触发。

🌐 Param callback functions are local to the router on which they are defined. They are not inherited by mounted apps or routers, nor are they triggered for route parameters inherited from parent routers. Hence, param callbacks defined on app will be triggered only by route parameters defined on app routes.

在参数出现的任何路由的任何处理程序之前,所有参数回调都会被调用,并且在一次请求-响应周期中,每个回调只会被调用一次,即使该参数在多个路由中匹配,如下例所示。

🌐 All param callbacks will be called before any handler of any route in which the param occurs, and they will each be called only once in a request-response cycle, even if the parameter is matched in multiple routes, as shown in the following examples.

app.param('id', function (req, res, next, id) {
console.log('CALLED ONLY ONCE');
next();
});
app.get('/user/:id', function (req, res, next) {
console.log('although this matches');
next();
});
app.get('/user/:id', function (req, res) {
console.log('and this matches too');
res.end();
});

GET /user/42 上,打印如下:

🌐 On GET /user/42, the following is printed:

CALLED ONLY ONCE
although this matches
and this matches too
app.param(['id', 'page'], function (req, res, next, value) {
console.log('CALLED ONLY ONCE with', value);
next();
});
app.get('/user/:id/:page', function (req, res, next) {
console.log('although this matches');
next();
});
app.get('/user/:id/:page', function (req, res) {
console.log('and this matches too');
res.end();
});

GET /user/42/3 上,打印如下:

🌐 On GET /user/42/3, the following is printed:

CALLED ONLY ONCE with 42
CALLED ONLY ONCE with 3
although this matches
and this matches too

Caution

以下部分描述了 app.param(callback),自 v4.11.0 起已弃用。

🌐 The following section describes app.param(callback), which is deprecated as of v4.11.0.

app.param(name, callback) 方法的行为可以完全通过仅向 app.param() 传递一个函数来更改。该函数是 app.param(name, callback) 应如何行为的自定义实现——它接受两个参数并且必须返回一个中间件。

🌐 The behavior of the app.param(name, callback) method can be altered entirely by passing only a function to app.param(). This function is a custom implementation of how app.param(name, callback) should behave - it accepts two parameters and must return a middleware.

此函数的第一个参数是应捕获的 URL 参数的名称,第二个参数可以是任何可能用于返回中间件实现的 JavaScript 对象。

🌐 The first parameter of this function is the name of the URL parameter that should be captured, the second parameter can be any JavaScript object which might be used for returning the middleware implementation.

该函数返回的中间件决定了当捕获到 URL 参数时会发生的行为。

🌐 The middleware returned by the function decides the behavior of what happens when a URL parameter is captured.

在这个例子中,app.param(name, callback) 的签名被修改为 app.param(name, accessId)app.param() 现在将接受一个名字和一个数字,而不是名字和回调函数。

🌐 In this example, the app.param(name, callback) signature is modified to app.param(name, accessId). Instead of accepting a name and a callback, app.param() will now accept a name and a number.

index.cjs
var express = require('express');
var app = express();
// customizing the behavior of app.param()
app.param(function (param, option) {
return function (req, res, next, val) {
if (val === option) {
next();
} else {
next('route');
}
};
});
// using the customized app.param()
app.param('id', 1337);
// route to trigger the capture
app.get('/user/:id', function (req, res) {
res.send('OK');
});
app.listen(3000, function () {
console.log('Ready');
});

在此示例中,app.param(name, callback) 签名保持不变,但不是使用中间件回调,而是定义了一个自定义的数据类型检查函数来验证用户 ID 的数据类型。

🌐 In this example, the app.param(name, callback) signature remains the same, but instead of a middleware callback, a custom data type checking function has been defined to validate the data type of the user id.

app.param(function (param, validator) {
return function (req, res, next, val) {
if (validator(val)) {
next();
} else {
next('route');
}
};
});
app.param('id', function (candidate) {
return !isNaN(parseFloat(candidate)) && isFinite(candidate);
});

Note

'.' 字符不能用来在你的捕获正则表达式中捕获字符。例如,你不能使用 '/user-.+/' 来捕获 'users-gami',请改用 [\s\S][\w\W](如同在 '/user-[\s\S]+/' 中一样)。

🌐 The ‘.’ character can’t be used to capture a character in your capturing regexp. For example you can’t use '/user-.+/' to capture 'users-gami', use [\s\S] or [\w\W] instead (as in '/user-[\s\S]+/'.

示例:

🌐 Examples:

// captures '1-a_6' but not '543-azser-sder'
router.get('/[0-9]+-[[\w]]*', function (req, res, next) {
next();
});
// captures '1-a_6' and '543-az(ser"-sder' but not '5-a s'
router.get('/[0-9]+-[[\S]]*', function (req, res, next) {
next();
});
// captures all (equivalent to '.*')
router.get('[[\s\S]]*', function (req, res, next) {
next();
});

app.path()

返回应用的规范路径,字符串。

🌐 Returns the canonical path of the app, a string.

index.js
var app = express();
var blog = express();
var blogAdmin = express();
app.use('/blog', blog);
blog.use('/admin', blogAdmin);
console.dir(app.path()); // ''
console.dir(blog.path()); // '/blog'
console.dir(blogAdmin.path()); // '/blog/admin'

在已挂载应用的复杂情况下,此方法的行为可能会变得非常复杂: 通常最好使用 req.baseUrl 来获取应用的规范路径。

🌐 The behavior of this method can become very complicated in complex cases of mounted apps: it is usually better to use req.baseUrl to get the canonical path of the app.

app.post()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

将 HTTP POST 请求路由到指定路径,并使用指定的回调函数。 更多信息,请参见 路由指南

🌐 Routes HTTP POST requests to the specified path with the specified callback functions. For more information, see the routing guide.

示例

🌐 Example

app.post('/', function (req, res) {
res.send('POST request to homepage');
});

app.put()

Arguments

path
Type:String | RegExp | Array

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

将 HTTP PUT 请求路由到指定路径,并使用指定的回调函数。

🌐 Routes HTTP PUT requests to the specified path with the specified callback functions.

示例

🌐 Example

app.put('/', function (req, res) {
res.send('PUT request to homepage');
});

app.render()

Arguments

view
Type:String

要渲染的视图的名称。

locals
Type:Object | undefined

一个对象,其属性定义视图的本地变量。

callback
Type:Function

回调函数以 callback(err, html) 的形式调用,并传入渲染的 HTML。

通过 callback 函数返回视图的渲染 HTML。它接受一个可选参数,该参数是包含视图局部变量的对象。它类似于 res.render(),只不过它不能单独将渲染后的视图发送给客户端。

🌐 Returns the rendered HTML of a view via the callback function. It accepts an optional parameter that is an object containing local variables for the view. It is like res.render(), except it cannot send the rendered view to the client on its own.

Note

app.render() 看作是用于生成渲染视图字符串的实用函数。在内部,res.render() 使用 app.render() 来渲染视图。

🌐 Think of app.render() as a utility function for generating rendered view strings. Internally res.render() uses app.render() to render views.

Warning

view 参数执行文件系统操作,例如从磁盘读取文件和评估 Node.js 模块,因此出于安全原因不应包含来自终端用户的输入。

🌐 The view argument performs file system operations like reading a file from disk and evaluating Node.js modules, and as so for security reasons should not contain input from the end-user.

Warning

locals 对象被视图引擎用于渲染响应。对象的键可能特别敏感,不应包含用户控制的输入,因为这可能影响视图引擎的操作或提供跨站脚本攻击的途径。有关更多注意事项,请查阅所使用视图引擎的文档。

🌐 The locals object is used by view engines to render a response. The object keys may be particularly sensitive and should not contain user-controlled input, as it may affect the operation of the view engine or provide a path to cross-site scripting. Consult the documentation for the used view engine for additional considerations.

Caution

本地变量 cache 用于启用视图缓存。如果你想在开发过程中缓存视图,请将其设置为 true;视图缓存在生产环境中默认已启用。

🌐 The local variable cache is reserved for enabling view cache. Set it to true, if you want to cache view during development; view caching is enabled in production by default.

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

app.route()

Arguments

path
Type:String

创建路由的路径。

返回单一路由的实例,然后你可以使用它来处理带有可选中间件的 HTTP 动词。使用 app.route() 可以避免路由名称重复(从而避免拼写错误)。

🌐 Returns an instance of a single route, which you can then use to handle HTTP verbs with optional middleware. Use app.route() to avoid duplicate route names (and thus typo errors).

var app = express();
app
.route('/events')
.all(function (req, res, next) {
// runs for all HTTP verbs first
// think of it as route specific middleware!
})
.get(function (req, res, next) {
res.json({});
})
.post(function (req, res, next) {
// maybe add a new event...
});

app.set()

Arguments

name
Type:String

要分配的设置名称。某些名称会配置服务器的行为;请参阅 应用设置表

value
Type:any

要分配给该设置的值。

将设置 name 分配为 value。你可以存储任何你想要的值,但某些名称可以用来配置服务器的行为。这些特殊名称列在 应用设置表 中。

🌐 Assigns setting name to value. You may store any value that you want, but certain names can be used to configure the behavior of the server. These special names are listed in the app settings table.

调用布尔属性的 app.set('foo', true) 与调用 app.enable('foo') 相同。同样,调用布尔属性的 app.set('foo', false) 与调用 app.disable('foo') 相同。

🌐 Calling app.set('foo', true) for a Boolean property is the same as calling app.enable('foo'). Similarly, calling app.set('foo', false) for a Boolean property is the same as calling app.disable('foo').

使用 app.get() 获取设置的值。

🌐 Retrieve the value of a setting with app.get().

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

应用设置

🌐 Application Settings

下表列出了应用设置。

🌐 The following table lists application settings.

Note

子应用将会:

🌐 Sub-apps will:

  • 不要继承具有默认值的设置。你必须在子应用中设置该值。
  • 继承没有默认值的设置;这些在下表中有明确标注。

例外情况:子应用将继承 trust proxy 的值,即使它有默认值(为了向后兼容); 子应用在生产环境中(当 NODE_ENV 为“生产”时)不会继承 view cache 的值。

🌐 Exceptions: Sub-apps will inherit the value of trust proxy even though it has a default value (for backward-compatibility); Sub-apps will not inherit the value of view cache in production (when NODE_ENV is “production”).

Settings

case sensitive routing
Type:BooleanDefault:N/A (undefined)

启用大小写敏感。启用后,“/Foo”和“/foo”是不同的路由。禁用后,“/Foo”和“/foo”被视为相同。

Note

子应用将继承此设置的值。

env
Type:StringDefault:process.env.NODE_ENV (NODE_ENV environment variable) or "development" if NODE_ENV is not set.

环境模式。在生产环境中务必设置为“production”;请参阅 生产最佳实践:性能与可靠性

etag
Type:Boolean | String | FunctionDefault:weak

设置 ETag 响应头。可能的取值请参见下表 etag 选项。 关于 HTTP ETag 头的更多信息

jsonp callback name
Type:StringDefault:"callback"

指定默认的 JSONP 回调函数名称。

json escape
Type:BooleanDefault:N/A (undefined)

启用对 res.jsonres.jsonpres.send API 的 JSON 响应进行转义。这将把字符 <>& 转义为 JSON 中的 Unicode 转义序列。这样做的目的是在客户端嗅探 HTML 响应时协助缓解某些类型的持久性 XSS 攻击

Note

子应用将继承此设置的值。

json replacer
Type:Function | ArrayDefault:N/A (undefined)

JSON.stringify 使用的 ‘replacer’ 参数

Note

子应用将继承此设置的值。

json spaces
Type:Number | StringDefault:N/A (undefined)

JSON.stringify 使用的 ‘space’ 参数。这通常设置为用于缩进美化 JSON 的空格数。

Note

子应用将继承此设置的值。

query parser
Type:Boolean | String | FunctionDefault:"extended"

通过将值设置为 false 来禁用查询解析,或者将查询解析器设置为使用“simple”或“extended”,或自定义查询字符串解析函数。简单查询解析器基于 Node 的原生查询解析器 querystring。扩展查询解析器基于 qs。自定义查询字符串解析函数将接收完整的查询字符串,并且必须返回查询键及其值的对象。

strict routing
Type:BooleanDefault:N/A (undefined)

启用严格路由。启用后,路由会将 “/foo” 和 “/foo/” 视为不同的路径。否则,路由会将 “/foo” 和 “/foo/” 视为相同的路径。

Note

子应用将继承此设置的值。

subdomain offset
Type:NumberDefault:2

要删除的主机的以点分隔的部分数以访问子域。

trust proxy
Type:Boolean | String | Number | Function | ArrayDefault:false (disabled)

表示应用位于前端代理之后,并使用 X-Forwarded-* 头来确定连接和客户端的 IP 地址。

Note

X-Forwarded-* 头容易被伪造,检测到的 IP 地址不可靠。

启用时,Express 会尝试确定通过前端代理或一系列代理连接的客户端的 IP 地址。req.ips 属性随后包含一个客户端通过其连接的 IP 地址数组。要启用它,请使用下表中信任代理选项描述的值。trust proxy 设置是使用 proxy-addr 包实现的。更多信息,请参见其文档。

Note

子应用将继承此设置的值,即使它有默认值。

views
Type:String | ArrayDefault:process.cwd() + '/views'

应用视图的目录或目录数组。如果是数组,视图将按数组中出现的顺序查找。

view cache
Type:BooleanDefault:true in production, otherwise undefined.

启用视图模板编译缓存。

Note

子应用在生产环境中不会继承此设置的值(当 NODE_ENV 为“production”时)。

view engine
Type:StringDefault:N/A (undefined)

省略时使用的默认引擎扩展。

Note

子应用将继承此设置的值。

x-powered-by
Type:BooleanDefault:true

启用 “X-Powered-By: Express” HTTP 头。

trust proxy 设置的选项

🌐 Options for trust proxy setting

阅读 Express behind proxies 以获取更多信息。

🌐 Read Express behind proxies for more information.

布尔值:如果为 true,则客户端的 IP 地址被理解为 X-Forwarded-* 头中的最左边条目。如果为 false,则应用被理解为直接面向互联网,客户端的 IP 地址来自 req.connection.remoteAddress。这是默认设置。

字符串 / 包含逗号分隔值的字符串 / 字符串数组:要信任的 IP 地址、子网或 IP 地址和子网的数组。预配置的子网名称如下:

  • 回环 - 127.0.0.1/8::1/128
  • 链路本地 - 169.254.0.0/16fe80::/10
  • uniquelocal - 10.0.0.0/8172.16.0.0/12192.168.0.0/16fc00::/7

可以通过以下任何方式设置IP地址:

🌐 Set IP addresses in any of the following ways:

指定一个子网:

🌐 Specify a single subnet:

app.set('trust proxy', 'loopback');

指定子网和地址:

🌐 Specify a subnet and an address:

app.set('trust proxy', 'loopback, 123.123.123.123');

以 CSV 格式指定多个子网:

🌐 Specify multiple subnets as CSV:

app.set('trust proxy', 'loopback, linklocal, uniquelocal');

将多个子网指定为数组:

🌐 Specify multiple subnets as an array:

app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);

在指定时,IP 地址或子网会从地址确定过程中排除,并将最接近应用服务器的不受信任的 IP 地址确定为客户端的 IP 地址。

🌐 When specified, the IP addresses or the subnets are excluded from the address determination process, and the untrusted IP address nearest to the application server is determined as the client’s IP address.

编号:将来自前置代理服务器的第n跳视为客户端。

功能:自定义信任实现。仅在你清楚自己在做什么时使用。

app.set('trust proxy', function (ip) {
if (ip === '127.0.0.1' || ip === '123.123.123.123')
return true; // trusted IPs
else return false;
});

etag 设置的选项

🌐 Options for etag setting

Note

这些设置仅适用于动态文件,而不适用于静态文件。express.static 中间件会忽略这些设置。

🌐 These settings apply only to dynamic files, not static files. The express.static middleware ignores these settings.

ETag 功能是使用 etag 包实现的。欲了解更多信息,请参阅其文档。

🌐 The ETag functionality is implemented using the etag package. For more information, see its documentation.

Values

Boolean

true 启用弱 ETag。这是默认设置。false 完全禁用 ETag。

String
如果为“strong”,则启用强 ETag。如果为“weak”,则启用弱 ETag。
Function

自定义 ETag 函数实现。仅在你知道自己在做什么时使用。

app.set('etag', function (body, encoding) {
return generateHash(body, encoding); // consider the function is defined
});

app.use()

Arguments

path
Type:String | RegExp | Array | undefined

The path for which the middleware function is invoked; can be any of: a string representing a path, a path pattern, a regular expression pattern to match paths, or an array of combinations of any of the above. For examples, see Path examples.

callback
Type:Function | Function[]

Callback functions; can be: a middleware function, a series of middleware functions (separated by commas), an array of middleware functions, or a combination of all of the above. You can provide multiple callback functions that behave just like middleware, except that these callbacks can invoke next('route') to bypass the remaining route callback(s). You can use this mechanism to impose pre-conditions on a route, then pass control to subsequent routes if there is no reason to proceed with the current route. Since router and app implement the middleware interface, you can use them as you would any other middleware function. For examples, see Middleware callback function examples.

在指定路径挂载指定的 middleware 函数或函数合集:当请求路径的基路径与 path 匹配时,会执行该中间件函数。

🌐 Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches path.

描述

🌐 Description

一个路由将匹配其路径后紧跟“/”的任何路径。例如:app.use('/apple', ...) 将匹配 “/apple”、“/apple/images”、“/apple/images/news” 等等。

🌐 A route will match any path that follows its path immediately with a “/”. For example: app.use('/apple', ...) will match “/apple”, “/apple/images”, “/apple/images/news”, and so on.

由于 path 的默认值是“/”,未指定路径的中间件将会在每个对应用的请求中执行。 例如,这个中间件函数将会在对应用的每一个请求中执行:

🌐 Since path defaults to ”/”, middleware mounted without a path will be executed for every request to the app. For example, this middleware function will be executed for every request to the app:

app.use(function (req, res, next) {
console.log('Time: %d', Date.now());
next();
});

Note

子应用将会:

🌐 Sub-apps will:

  • 不要继承具有默认值的设置。你必须在子应用中设置该值。
  • 继承没有默认值的设置的值。

有关详细信息,请参见 应用设置

🌐 For details, see Application settings.

中间件函数是按顺序执行的,因此中间件的包含顺序非常重要。

🌐 Middleware functions are executed sequentially, therefore the order of middleware inclusion is important.

// this middleware will not allow the request to go beyond it
app.use(function (req, res, next) {
res.send('Hello World');
});
// requests will never reach this route
app.get('/', function (req, res) {
res.send('Welcome');
});

错误处理中间件

错误处理中间件始终接受_四个_参数。你必须提供四个参数来将其标识为错误处理中间件函数。即使你不需要使用 next 对象,也必须指定它以保持签名。否则,next 对象将被解释为普通中间件,并且无法处理错误。有关错误处理中间件的详细信息,请参见:错误处理

🌐 Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next object, you must specify it to maintain the signature. Otherwise, the next object will be interpreted as regular middleware and will fail to handle errors. For details about error-handling middleware, see: Error handling.

像定义其他中间件函数一样定义错误处理中间件函数,只是有四个参数而不是三个,特别是其签名为 (err, req, res, next)):

🌐 Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature (err, req, res, next)):

app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

路径示例

🌐 Path examples

下表提供了一些用于挂载中间件的有效 path 值的简单示例。

🌐 The following table provides some simple examples of valid path values for mounting middleware.

路径:匹配精确路径 /abcd 以及以 /abcd/ 开头的任何子路径(例如,/abcd/foo):

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

路径模式:这将匹配以 /abcd/abd 开头的路径:

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

这将匹配以 /abcd/abbcd/abbbbbcd 等开头的路径:

🌐 This will match paths starting with /abcd, /abbcd, /abbbbbcd, and so on:

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

这将匹配以 /abcd/abxcd/abFOOcd/abbArcd 等开头的路径:

🌐 This will match paths starting with /abcd, /abxcd, /abFOOcd, /abbArcd, and so on:

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

这将匹配以 /ad/abcd 开头的路径:

🌐 This will match paths starting with /ad and /abcd:

app.use('/a(bc)?d', 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();
});

中间件回调函数示例

🌐 Middleware callback function examples

下表提供了一些可以作为 callback 参数传递给 app.use()app.METHOD()app.all() 的中间件函数的简单示例。

🌐 The following table provides some simple examples of middleware functions that can be used as the callback argument to app.use(), app.METHOD(), and app.all().

单一中间件:你可以在本地定义并挂载一个中间件函数。

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

路由是有效的中间件。

🌐 A router is valid middleware.

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

一个 Express 应用是有效的中间件。

🌐 An Express app is valid middleware.

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);

以下是一些在 Express 应用中使用 express.static 中间件的示例。

🌐 Following are some examples of using the express.static middleware in an Express app.

从应用目录中的“public”目录为应用提供静态内容:

🌐 Serve static content for the app from the “public” directory in the application directory:

// GET /style.css etc
app.use(express.static(path.join(__dirname, 'public')));

将中间件挂载在 “/static”,仅当请求路径以 “/static” 为前缀时提供静态内容:

🌐 Mount the middleware at “/static” to serve static content only when their request path is prefixed with “/static”:

// GET /static/style.css etc.
app.use('/static', express.static(path.join(__dirname, 'public')));

通过在静态中间件之后加载日志中间件来禁用对静态内容请求的日志记录:

🌐 Disable logging for static content requests by loading the logger middleware after the static middleware:

app.use(express.static(path.join(__dirname, 'public')));
app.use(logger());

从多个目录提供静态文件,但优先使用 ”./public” 而不是其他目录:

🌐 Serve static files from multiple directories, but give 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')));