express中间件原理 && 实现
Posted Wayne Zhu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了express中间件原理 && 实现相关的知识,希望对你有一定的参考价值。
一、什么是express中间件?
什么是express中间件呢? 我们肯定都听说过这个词,并且,如果你用过express,那么你就一定用过express中间件,如下:
var express = require(‘express‘); var app = express(); app.listen(3000, function () { console.log(‘listening 3000‘) }); app.use(middleware1); app.use(middleware2); app.use(middleware3);
是的, middleware1、middleware2、middleware3就是中间件了,我们使用app.use,就是在使用这个中间件。
即中间件的使用方法就是 app.use(middleware)。
那么究竟什么是中间件?
这里有一个详尽的解释: http://expressjs.com/en/guide/using-middleware.html, 大致如下:
Middleware functions are functions that have access to the request object (
req
), the response object (res
), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable namednext
.
当一个请求发送到服务器之后,服务器接收到请求,然后开始处理,处理完之后返回响应。 处理的时候就会用到中间件了,使用中间件的顺序就是app.use(middleware)的顺序。
比如: 之前我在写websocket聊天室时使用的中间件大致如下:
let express = require(‘express‘) let path = require(‘path‘) let app = express() // 省略若干。。。 // 用于解析 body。 let bodyParser = require("body-parser"); // 基于express实例创建http服务器 let server = require(‘http‘).Server(app); // 创建websocket服务器,以监听http服务器 let io = require(‘socket.io‘).listen(server); // 引入路由 let route = require(‘../router/index.js‘); // 使用devMiddleware app.use(devMiddleware) // 使用hotMiddleware app.use(hotMiddleware) // 使用使用了body-parser模块,才能通过 req.body 接受到post表单里的内容。 app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); // node你服务器使用的静态文件 app.use(‘/‘, express.static(‘./www‘)) // 使用路由 app.use(‘/‘, route); // 开启node后台服务器 console.log(‘> Starting dev server...‘) let port = ‘3000‘; server.listen(port, function () { console.log(‘The server is listening at localhost:‘ + port) }); app.get(‘*‘, function (request, response){ response.sendFile(path.resolve(__dirname, ‘../www/index.html‘)) }) // 引入服务器端websocket处理代码 let Websocket = require(‘./websocket.js‘); // 执行 Websocket(io);
这里为了表示清楚,省略了一些代码,然后可以看到服务器端接收到请求之后使用的顺序大致如下:
- 使用devMiddleware
- 使用hotMiddleware
- 使用body-parser解析
- 使用静态文件
- 使用路由
这里的顺序还是很清楚的,即请求来了之后,会依次通过各个中间件进行处理,处理完成之后,就next(),把控制权交给下一个中间件,到了最后,我们就可以很好的使用路由了。比如之前使用了body-parser中间件,后面在路由中我们就可以使用 req.body 来处理了。
二、为什么要使用express中间件(好处)?
我们可以总结为下面的几个好处:
- 逻辑清楚,层次分明。 正如TCP/IP中的分层一样,通过分层,可以使得每一个部分各司其职,更好的干事情。
- 便于维护。如果觉得其中一个做的不好,还可以换一个中间件,而其他的不用替换。
- 可复用。我们写好了一个中间件之后,就可以直接拿来在别的地方用了,就比如,我们在使用第三方中间件的时候,直接npm install somemiddleware,然后 require,最后直接 app.use 即可,非常方便。
三、中间件的本质是什么?
实际上,中间件就是一个函数,这个函数可以接受三个参数,req、res、next, 其中req即客户端发送过来的请求,res即可以进行相应,next即后面还有其他的中间件,通过next可以交出req、res的控制权,后续的中间件继续处理。
如下所示:
function middleware(req,res,next){ // 对req、res进行处理 // 如果后面还有中间件,那么就交出控制权,后面的中间件继续对请求进行处理。 next(); }
另外,next实际上也是一个函数,这里通过next()调用,就可以成功地交出控制权了。
我们可以建立一个express,然后在index.js中代码如下:
var express = require(‘express‘); var app = express(); app.use(function (req, res, next) { console.log(req) next(); }); app.get(‘/‘, function (req, res) { res.send(‘哈哈‘); res.end(); }); app.listen(3000, function () { console.log(‘listening 3000‘) });
这时,可以发现: 当我们启动服务器的时候,请求localhost:3000, 然后在后台就会打印出这个请求的详细信息,然后,next(),就可以接着被get这个中间件处理,向客户端发送“哈哈”, 最后,res.end(),即返回响应,后面不再有中间件了。
注意:如果在第一个中间中没有next(), 那么这个请求就会被 hang ,后续的get中间件就无法使用了,所以,对于中间位置的中间件必须调用next()函数把这个控制权交出来。对于一些HotMiddleware和devMilddleware,他们也都是在最后需要交出控制权的。
四、express中间件的分类。
那么express中间件是如何分类的呢?
一般,express应用可以使用下面的几类中间件:
- Application-level middleware 应用级中间件
- Router-level middleware 路由级中间件
- Error-handling middleware 错误处理级中间件
- Built-in middleware 内置中间件
- Third-party middleware 第三方中间件
4.1 Application-level middleware
var app = express() app.use(function (req, res, next) { console.log(‘Time:‘, Date.now()) next() })
这就是一个应用级的中间件,即对于请求,我们可以对之进行相应的处理。
app.use(‘/user/:id‘, function (req, res, next) { console.log(‘Request Type:‘, req.method) next() })
这个例子是当相应的请求执行时,我们所做的处理。
4.2 Router-level middleware
路由级中间件和一般的应用级中间件的使用方法是一样:
var router = express.Router()
下面是一个例子:
var app = express() var router = express.Router() // a middleware function with no mount path. This code is executed for every request to the router router.use(function (req, res, next) { console.log(‘Time:‘, Date.now()) next() }) // a middleware sub-stack shows request info for any type of HTTP request to the /user/:id path router.use(‘/user/:id‘, function (req, res, next) { console.log(‘Request URL:‘, req.originalUrl) next() }, function (req, res, next) { console.log(‘Request Type:‘, req.method) next() }) // a middleware sub-stack that handles GET requests to the /user/:id path router.get(‘/user/:id‘, function (req, res, next) { // if the user ID is 0, skip to the next router if (req.params.id === ‘0‘) next(‘route‘) // otherwise pass control to the next middleware function in this stack else next() }, function (req, res, next) { // render a regular page res.render(‘regular‘) }) // handler for the /user/:id path, which renders a special page router.get(‘/user/:id‘, function (req, res, next) { console.log(req.params.id) res.render(‘special‘) }) // mount the router on the app app.use(‘/‘, router)
4.3 Error-handling middleware
错误处理中间件总是会有四个参数,所以你必须提供四个参数来表明这个中间件是一个错误处理中间件,即使也许你不需要Next对象,你也得明确说明,否则的话,这就会被解析为一个普通的中间件,而非错误处理中间件了。
下面就是一个错误处理中间件了:
app.use(function (err, req, res, next) { console.error(err.stack) res.status(500).send(‘Something broke!‘) })
4.4 Built-in middleware
从版本4.4开始,express已经不再依赖于 connect 。
Express中唯一的一个内置中间件就是 express.static 了, 这个函数是基于 server-static 的,负责提供类似于html、css、img、js等静态文件的。
这个内置中间件的使用方式如下:
express.static(root, [options])
root就是提供静态文件的目录。
下面是一个简单的使用express.static中间件的例子:
var options = { dotfiles: ‘ignore‘, etag: false, extensions: [‘htm‘, ‘html‘], index: false, maxAge: ‘1d‘, redirect: false, setHeaders: function (res, path, stat) { res.set(‘x-timestamp‘, Date.now()) } } app.use(express.static(‘public‘, options))
在一个app里,你还可以使用多个静态文件目录:
app.use(express.static(‘public‘)) app.use(express.static(‘uploads‘)) app.use(express.static(‘files‘))
4.5 Third-party middleware
第三方中间件我们使用的非常多,比如cookie-parser、body-parser等等,下面是一个使用了第三方中间件的例子:
var express = require(‘express‘) var app = express() var cookieParser = require(‘cookie-parser‘) // load the cookie-parsing middleware app.use(cookieParser())
五、express中间件的简单使用。
六、实现一个简单的express中间件。
参考文章: http://expressjs.com/en/guide/using-middleware.html
以上是关于express中间件原理 && 实现的主要内容,如果未能解决你的问题,请参考以下文章