使用中间件
Express是一个路由和中间件Web框架,其自身的功能很少:Express应用程序本质上是一系列中间件函数调用。
中间件功能是可以访问请求对象 (req
),响应对象(res
)和应用程序的请求-响应周期中的下一个中间件功能的功能。下一个中间件功能通常由名为的变量表示next
。
中间件功能可以执行以下任务:
- 执行任何代码。
- 更改请求和响应对象。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
如果当前的中间件功能没有结束请求-响应周期,则必须调用next()
将控制权传递给下一个中间件功能。否则,该请求将被挂起。
Express应用程序可以使用以下类型的中间件:
您可以使用可选的安装路径来加载应用程序级和路由器级中间件。您还可以将一系列中间件功能一起加载,从而在安装点创建中间件系统的子堆栈。
应用层中间件
var express = require('express');
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(express.static(__dirname)); // 设置静态文件目录,外网可直接访问。
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
app.use(function(req, res, next) {
console.log('use1');
next();
})
/**
* 输出6次
* use1
* cb0
* cb1
* next
* next2
* send
*/
app.get('/test', [cb0, cb1], function(req, res, next){
console.log('next');
next();
},
function(req, res, next) {
console.log('next2')
next();
},
function(req, res) {
console.log('send');
res.send('hello world!!');
});
/**
* 要从路由器中间件堆栈中跳过其余中间件功能,请调用next('route')将控制权传递给下一条路由。
* 注意:next('route')仅在使用app.METHOD()或router.METHOD()函数加载的中间件函数中有效。
* req.params.id !== 0的情况下输出下面内容
* use1
*/
app.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id === '0') next('route')
// otherwise pass the control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// send a regular response
res.send('regular')
});
app.use(function(req, res, next) {
console.log('use2');
next();
})
/**
* 当req.params.id === '0' 输出下面内容
* use1
* use2
*/
app.get('/user/:id', function (req, res, next) {
res.send('special')
})
/**
* 如果访问 /test 地址,此use不会被执行
*/
app.use(function(req, res, next) {
console.log('use3');
next();
});
app.listen(app.get('port'), function () {
console.log(`Express started on http://localhost:${app.get('port')}; press Ctrl + c to terminate.`);
});
路由器级中间件
路由器级中间件与应用程序级中间件的工作方式相同,只不过它绑定到的实例express.Router()
。
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
错误处理中间件
错误处理中间件始终采用四个参数。您必须提供四个参数以将其标识为错误处理中间件函数。即使不需要使用该next
对象,也必须指定该对象以维护签名。否则,该next
对象将被解释为常规中间件,并且将无法处理错误。
定义错误处理中间件函数的方式与其他中间件函数相同,除了使用四个参数而不是三个参数(特别是使用签名(err, req, res, next)
)之外:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
有关错误处理中间件的详细信息,请参见:错误处理。
内置中间件
从版本4.x开始,Express不再依赖Connect。Express以前包含的中间件功能现在位于单独的模块中;请参阅中间件功能列表。
Express具有以下内置的中间件功能:
- express.static提供静态资源,例如HTML文件,图像等。
- express.json使用JSON负载解析传入的请求。注意:Express 4.16.0+中可用
- express.urlencoded使用URL编码的有效内容解析传入的请求。 注意:Express 4.16.0+中可用
第三方中间件
使用第三方中间件向Express应用程序添加功能。
安装Node.js模块以获得所需的功能,然后在应用程序级别或路由器级别将其加载到您的应用程序中。
以下示例说明了如何安装和加载cookie解析中间件功能cookie-parser
。$ npm install cookie-parser
有关Express常用的第三方中间件功能的部分列表,请参阅:第三方中间件。var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
// load the cookie-parsing middleware
app.use(cookieParser())
Demo cookie-parser中间件的使用
var express = require('express');
var cookie = require('cookie');
var cookieParser = require('cookie-parser');
var app = express();
app.set('port', process.env.PORT || 3000);
app.use(express.static(__dirname)); // 设置静态文件目录,外网可直接访问。
app.use(cookieParser('123456')); //使用cookie中间件,传入签名123456进行加密
app.use(function(req, res, next) {
/**
* cookie.parse(str: string, options?: {
* decode = decodeURIComponent
* });
* options.decode
* 指定一个函数,该函数将用于解码cookie的值,
* 由于cookie的值具有有限的字符集(并且必须是简单的字符串),
* 因此可以使用此函数将先前编码的cookie值解码为JavaScript字符串或其他对象,
* 默认函数是global decodeURIComponent,它将所有URL编码序列解码为它们的字节表示形式。
*/
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2' }
var date = new Date();
var expireHours = 1;
/**
* cookie.serialize(name: string, value: string, options?: {
* domain?: '/test', // 指定域名,不指定就时当前域
* encode?: encodeURIComponent, // 指定一个编码cookie的函数,默认是encodeURIComponent
* expires?: (new Date()).setTime() // 过期时间
* // cookie存储模型规范指出,
* // 如果同时设置了expires和 maxAge,则maxAge具有优先权,
* // 但是可能并非所有客户端都遵守此规定,因此,如果同时设置了这两个客户端,
* // 则它们应指向相同 的日期和时间。
* httpOnly?: true, // 默认为false, 建议设置为true, 客户端将无法通过document.cookie读取到 COOKIE 信息,可防止 XSS 攻击产生
* maxAge?: expireHours * 3600 * 1000, // number 以秒为时间
* secure?: false, // 设置为true时,cookie在http中失效,只能在https中使用
* path?: '/test', // 表示cookie影响到的路由,如 path=/。如果路径不能匹配时,浏览器则不发送这个 Cookie
* sameSite?: false,
* // 将boolean或指定string为SameSite Set-Cookie属性的值,
* // true会将SameSite属性设置为,Strict以严格执行相同的网站,
* // false不会设置SameSite属性。
* // 'lax'会将SameSite属性设置为,以Lax实现宽松的同一网站实施。
* // 'none'会将SameSite属性设置为,None用于显式的跨站点Cookie。
* // 'strict'会将SameSite属性设置为,Strict以严格执行相同的网站。
* // 注意这是一个尚未完全标准化的属性,将来可能会更改。这也意味着许多客户端可能会忽略此属性,直到他们了解它为止。
* signed: true
* // 表示是否签名(加密) cookie, 设为 true 会对这个 cookie 签名,这样就需要用res.signedCookies 访问它,
* // 前提需要设置上面中间件app.use传参 未签名则用 res.cookies 访问。
* })
* 将Cookie名称/值对序列化为Set-Cookie 格式字符串。
*/
var setCookie = cookie.serialize('foo', 'bar', {
expires: date.setTime(date.getTime() + expireHours * 3600 * 1000), // 3600 * 1000 表示 60分钟
}); // foo=bar
console.log(cookies);
console.log(setCookie);
next();
})
app.get('/test', function (req, res) {
console.log('Cookies: ', req.cookies)
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies)
res.send('hello world!!');
})
app.listen(app.get('port'), function () {
console.log(`Express started on http://localhost:${app.get('port')}; press Ctrl + c to terminate.`);
});