Node.js 中心件的工作道理
什么是 Express 中心件?
- 中心件在字面上的意思是你在软件的一层和另一层中心放置的任何东西。
- Express 中心件是在对 Express 效劳器恳求的生命周期内所施行的函数。
- 每个中心件都可以拜访其被附加到的所有路由的 HTTP 恳求和响应。
- 别的,中心件可以终止 HTTP 恳求,也可以用 next 将其传递给另一个中心件函数。中心件的这种“链”使你可以对代码停止划分并创立可重用的中心件。
编写 Express 中心件的要求
你需要安置一些东西来创立、使用和测试 Express 中心件。第一需要 Node 和 NPM。为确保已经安置,可以运转:
npm -v && node -v
你应当看到已安置的 Node 和 NPM 版本。假如显现错误,则需要安置 Node。所有例子都应在 Node ver 8+ 和NPM ver 5+ 下使用。
本文使用了 Express 4.x 版。这很重要,由于从 3.x 版到 4.x 版有严重的更换。
Express中心件:根基
第一我们使用 Express 最根本的内置中心件。创立一个新项目并 npm 初始化它…
npm init npm install express --save Create server.js and paste the following code: const express = require('express'); const app = express(); app.get('/', (req, res, next) => { res.send('Welcome Home'); }); app.listen(3000);
中心件解决什么问题?为什么要用它?
假设你在 web 网络效劳器上正在使用 Node.js 和 Express 运转Web利用程序。在此利用中,你需要登录的某些页面。
当 Web 效劳器收到数据恳求时,Express 将为你供给一个恳求对象,其中包括有关会员及其所恳求数据的信息。 Express 还使你可以拜访响应对象,可以在Web效劳器响利用户此前对其停止修改。这些对象平常缩短为 req,res。
中心件函数是使用相关信息修改 req 和 res 对象的抱负场合。例如会员登录后,你可以从数据库中猎取其会员具体信息,然后将这些具体信息储备在 res.user
中。
中心件函数是啥样的?
async function userMiddleware (req, res, next) { try { const userData = await getUserData(req.params.id); //see app.get below if(userData) { req.user = userData; next(); } } catch(error) { res.status(500).send(error.message); //replace with proper error handling } }
假如显现错误,并且你不想施行其他代码,则不要调取该函数。请记住在这种状况下要发送响应,不然客户端将会等候响应直到超时。
var app = express(); //your normal route Handlers app.get('/user/:id', userMiddleware, userController);
中心件链
你可以在中心件数组中或着通过使用多个 app.use
调取来链接中心件:
app.use(middlewareA); app.use(middlewareB); app.get('/', [middlewareC, middlewareD], handler);
Express 收到恳求后,与恳求相匹配的每个中心件都将会依照初始化的次序运转,直到有终止操纵为止。
因此,假如发生错误,则将按次序调取所有用于处置错误的中心件,直到其中一个不再调取 next() 函数调取为止。
Express中心件的类型
- 路由器级中心件,例如:router.use
- 内置中心件,例如:express.static,express.json,express.urlencoded
- 错误处置中心件,例如:app.use(err,req,res,next)
- 第三方中心件,例如:bodyparser、cookieparser
- 路由器级中心件
express.Router 使用 express.Router 类创立模块化的、可安置的路由处置。路由实例是一个完全的中心件和路由系统。
- 你可以用中心件停止日志记载、身份验证等操纵。如下所示,以记载会员的最新活动并解析身份验证标头,用它肯定当前登录的会员并将其增加到 Request 对象。
- 该函数在程序每次收到恳求时施行。假如有错误,它会仅完毕响应,而不会调取后续的中心件或路由处置。
var router = express.Router() //Load router-level middleware by using the router.use() and router.METHOD() functions. //The following example creates a router as a module, loads a middleware function in it, // defines some routes, and mounts the router module on a path in the main app. var express = require(‘express’); var router = express.Router(); // a middleware function with no mount path. This code is executed for // every request to the router // logging async function logMiddleware (req, res, next) { try { console.log(req.user.id, new Date()); next(); } catch() { res.status(500).send(error.message); } } // authentication async function checkAuthentication(req, res, next) => { // check header or url parameters or post parameters for token const token = req.body.token || req.query.token || req.headers['x-access-token'] || req.headers['authorization']; if (token) { try { // verifies secret req.decoded = await jwt.verify(token, config.secret) let checkUser = await authenticateTokenHelper.getUserDetail(req); // if everything is good, save to request for use in other routes if (checkUser) { req.user = req.decoded next() } else { return res.status(403).json({ message: responseMessage.noAuthorized }) } } catch (err) { return res.status(401).json({ message: responseMessage.invalidToken }) } } else { // if there is no token return res.status(400).json({ message: responseMessage.invalidRequest }) } } router.use(logMiddleware); router.get('/user, checkAuthentication, handler);
内置中心件
Express 有以下内置的中心件功效:
express.static
供给静态资源,例如 HTML 文件,图像等。express.json
负载解析用 JSON 传入的恳求。express.urlencoded
解析传入的用 URL 编码的有效载荷恳求。
错误处置中心件
错误处置中心件始终采纳四个参数(err,req,res,next)。你必需通过供给四个参数来将其标识为错误处置中心件函数。即便你不需要使用 next 对象,也必需指定。不然 next 对象将被说明为常规中心件,并将会没法处置错误。根本签名如下所示:
app.use(function (err, req, res, next) { console.error(err.stack) res.status(500).send('Something broke!') })
例1:
app.get('/users', (req, res, next) => { next(new Error('I am passing you an error!')); }); app.use((err, req, res, next) => { console.log(err); if(!res.headersSent){ res.status(500).send(err.message); } });
在这种状况下,管道末端的错误处置中心件将会处置该错误。你大概还会留意到,我检查了 res.headersSent
属性。这只是检查响应可否已经将标头发送到客户端。假如还没有,它将向客户端发送 HTTP 500 状态和错误新闻。
例2:
你还可以链接错误处置中心件。平常以不一样的方式处置不一样类型的错误:
app.get('/users, (req, res, next) => { let err = new Error('I couldn\'t find it.'); err.httpStatusCode = 404; next(err); }); app.get('/user, (req, res, next) => { let err = new Error('I\'m sorry, you can\'t do that, Dave.'); err.httpStatusCode = 304; next(err); }); app.use((err, req, res, next) => { // handles not found errors if (err.httpStatusCode === 404) { res.status(400).render('NotFound'); } // handles unauthorized errors else if(err.httpStatusCode === 304){ res.status(304).render('Unauthorized'); } // catch all else if (!res.headersSent) { res.status(err.httpStatusCode || 500).render('UnknownError'); } next(err); });
- 在这种状况下,中心件检查可否抛出了 404(not found)错误。假如是,它将渲染 “NotFound” 模板页面,然后将错误传递到中心件中的下一项。
- 下一个中心件检查可否抛出了 304(unauthorized)错误。假如是,它将渲染“Unauthorized”页面,并将错误传递到管道中的下一个中心件。
- 最后,“catch all” 错误处置仅记载错误,假如未发送响应,它将发送错误的 httpStatusCode(假如未供给则发送 HTTP 500 状态)并渲染 “UnknownError” 模板。
第三方级别的中心件
在某些状况下,我们将向后端增加一些额外的功效。先安置 Node.js 模块猎取所需的功效,然后在利用级别或路由器级别将其加载到你的利用中。
示例:当 body-parser 处置 Content-Type 恳求标头时,所有中心件都将使用解析的正文填充 req.body
属性。
const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(bodyParser.urlencoded({extended:false})) app.use(bodyParser.json()) app.post('/save',(req,res)=>{ res.json({ "status":true, "payload":req.body }) } app.listen(3000,(req,res)=>{ console.log('server running on port') })
总结
中心件功效是一种非常好的方式,可以对每个恳求或针对特定路由的每个恳求运转代码,并对恳求或响应数据采取办法。中心件是现代 Web 效劳器的重要组成部分,并且非常有用。