使用中间件

Express是一个路由和中间件Web框架,其自身的功能很少:Express应用程序本质上是一系列中间件函数调用。
中间件功能是可以访问请求对象req),响应对象res)和应用程序的请求-响应周期中的下一个中间件功能的功能。下一个中间件功能通常由名为的变量表示next
中间件功能可以执行以下任务:

  • 执行任何代码。
  • 更改请求和响应对象。
  • 结束请求-响应周期。
  • 调用堆栈中的下一个中间件函数。

如果当前的中间件功能没有结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件功能。否则,该请求将被挂起。
Express应用程序可以使用以下类型的中间件:

您可以使用可选的安装路径来加载应用程序级和路由器级中间件。您还可以将一系列中间件功能一起加载,从而在安装点创建中间件系统的子堆栈。

应用层中间件

  1. var express = require('express');
  2. var app = express();
  3. app.set('port', process.env.PORT || 3000);
  4. app.use(express.static(__dirname)); // 设置静态文件目录,外网可直接访问。
  5. var cb0 = function (req, res, next) {
  6. console.log('CB0')
  7. next()
  8. }
  9. var cb1 = function (req, res, next) {
  10. console.log('CB1')
  11. next()
  12. }
  13. app.use(function(req, res, next) {
  14. console.log('use1');
  15. next();
  16. })
  17. /**
  18. * 输出6次
  19. * use1
  20. * cb0
  21. * cb1
  22. * next
  23. * next2
  24. * send
  25. */
  26. app.get('/test', [cb0, cb1], function(req, res, next){
  27. console.log('next');
  28. next();
  29. },
  30. function(req, res, next) {
  31. console.log('next2')
  32. next();
  33. },
  34. function(req, res) {
  35. console.log('send');
  36. res.send('hello world!!');
  37. });
  38. /**
  39. * 要从路由器中间件堆栈中跳过其余中间件功能,请调用next('route')将控制权传递给下一条路由。
  40. * 注意:next('route')仅在使用app.METHOD()或router.METHOD()函数加载的中间件函数中有效。
  41. * req.params.id !== 0的情况下输出下面内容
  42. * use1
  43. */
  44. app.get('/user/:id', function (req, res, next) {
  45. // if the user ID is 0, skip to the next route
  46. if (req.params.id === '0') next('route')
  47. // otherwise pass the control to the next middleware function in this stack
  48. else next()
  49. }, function (req, res, next) {
  50. // send a regular response
  51. res.send('regular')
  52. });
  53. app.use(function(req, res, next) {
  54. console.log('use2');
  55. next();
  56. })
  57. /**
  58. * 当req.params.id === '0' 输出下面内容
  59. * use1
  60. * use2
  61. */
  62. app.get('/user/:id', function (req, res, next) {
  63. res.send('special')
  64. })
  65. /**
  66. * 如果访问 /test 地址,此use不会被执行
  67. */
  68. app.use(function(req, res, next) {
  69. console.log('use3');
  70. next();
  71. });
  72. app.listen(app.get('port'), function () {
  73. console.log(`Express started on http://localhost:${app.get('port')}; press Ctrl + c to terminate.`);
  74. });

路由器级中间件

路由器级中间件与应用程序级中间件的工作方式相同,只不过它绑定到的实例express.Router()

  1. var express = require('express')
  2. var router = express.Router()
  3. // middleware that is specific to this router
  4. router.use(function timeLog (req, res, next) {
  5. console.log('Time: ', Date.now())
  6. next()
  7. })
  8. // define the home page route
  9. router.get('/', function (req, res) {
  10. res.send('Birds home page')
  11. })
  12. // define the about route
  13. router.get('/about', function (req, res) {
  14. res.send('About birds')
  15. })
  16. module.exports = router

错误处理中间件

错误处理中间件始终采用四个参数。您必须提供四个参数以将其标识为错误处理中间件函数。即使不需要使用该next对象,也必须指定该对象以维护签名。否则,该next对象将被解释为常规中间件,并且将无法处理错误。
定义错误处理中间件函数的方式与其他中间件函数相同,除了使用四个参数而不是三个参数(特别是使用签名(err, req, res, next))之外:

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

有关错误处理中间件的详细信息,请参见:错误处理

内置中间件

从版本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
    1. $ npm install cookie-parser
    1. var express = require('express')
    2. var app = express()
    3. var cookieParser = require('cookie-parser')
    4. // load the cookie-parsing middleware
    5. app.use(cookieParser())
    有关Express常用的第三方中间件功能的部分列表,请参阅:第三方中间件

Demo cookie-parser中间件的使用

  1. var express = require('express');
  2. var cookie = require('cookie');
  3. var cookieParser = require('cookie-parser');
  4. var app = express();
  5. app.set('port', process.env.PORT || 3000);
  6. app.use(express.static(__dirname)); // 设置静态文件目录,外网可直接访问。
  7. app.use(cookieParser('123456')); //使用cookie中间件,传入签名123456进行加密
  8. app.use(function(req, res, next) {
  9. /**
  10. * cookie.parse(str: string, options?: {
  11. * decode = decodeURIComponent
  12. * });
  13. * options.decode
  14. * 指定一个函数,该函数将用于解码cookie的值,
  15. * 由于cookie的值具有有限的字符集(并且必须是简单的字符串),
  16. * 因此可以使用此函数将先前编码的cookie值解码为JavaScript字符串或其他对象,
  17. * 默认函数是global decodeURIComponent,它将所有URL编码序列解码为它们的字节表示形式。
  18. */
  19. var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2'); // { foo: 'bar', equation: 'E=mc^2' }
  20. var date = new Date();
  21. var expireHours = 1;
  22. /**
  23. * cookie.serialize(name: string, value: string, options?: {
  24. * domain?: '/test', // 指定域名,不指定就时当前域
  25. * encode?: encodeURIComponent, // 指定一个编码cookie的函数,默认是encodeURIComponent
  26. * expires?: (new Date()).setTime() // 过期时间
  27. * // cookie存储模型规范指出,
  28. * // 如果同时设置了expires和 maxAge,则maxAge具有优先权,
  29. * // 但是可能并非所有客户端都遵守此规定,因此,如果同时设置了这两个客户端,
  30. * // 则它们应指向相同 的日期和时间。
  31. * httpOnly?: true, // 默认为false, 建议设置为true, 客户端将无法通过document.cookie读取到 COOKIE 信息,可防止 XSS 攻击产生
  32. * maxAge?: expireHours * 3600 * 1000, // number 以秒为时间
  33. * secure?: false, // 设置为true时,cookie在http中失效,只能在https中使用
  34. * path?: '/test', // 表示cookie影响到的路由,如 path=/。如果路径不能匹配时,浏览器则不发送这个 Cookie
  35. * sameSite?: false,
  36. * // 将boolean或指定string为SameSite Set-Cookie属性的值,
  37. * // true会将SameSite属性设置为,Strict以严格执行相同的网站,
  38. * // false不会设置SameSite属性。
  39. * // 'lax'会将SameSite属性设置为,以Lax实现宽松的同一网站实施。
  40. * // 'none'会将SameSite属性设置为,None用于显式的跨站点Cookie。
  41. * // 'strict'会将SameSite属性设置为,Strict以严格执行相同的网站。
  42. * // 注意这是一个尚未完全标准化的属性,将来可能会更改。这也意味着许多客户端可能会忽略此属性,直到他们了解它为止。
  43. * signed: true
  44. * // 表示是否签名(加密) cookie, 设为 true 会对这个 cookie 签名,这样就需要用res.signedCookies 访问它,
  45. * // 前提需要设置上面中间件app.use传参 未签名则用 res.cookies 访问。
  46. * })
  47. * 将Cookie名称/值对序列化为Set-Cookie 格式字符串。
  48. */
  49. var setCookie = cookie.serialize('foo', 'bar', {
  50. expires: date.setTime(date.getTime() + expireHours * 3600 * 1000), // 3600 * 1000 表示 60分钟
  51. }); // foo=bar
  52. console.log(cookies);
  53. console.log(setCookie);
  54. next();
  55. })
  56. app.get('/test', function (req, res) {
  57. console.log('Cookies: ', req.cookies)
  58. // Cookies that have been signed
  59. console.log('Signed Cookies: ', req.signedCookies)
  60. res.send('hello world!!');
  61. })
  62. app.listen(app.get('port'), function () {
  63. console.log(`Express started on http://localhost:${app.get('port')}; press Ctrl + c to terminate.`);
  64. });