詳解Node.js中間件是怎樣工作的
什么是 Express 中間件?
- 中間件在字面上的意思是你在軟件的一層和另一層中間放置的任何東西。
- Express 中間件是在對(duì) Express 服務(wù)器請(qǐng)求的生命周期內(nèi)所執(zhí)行的函數(shù)。
- 每個(gè)中間件都可以訪問(wèn)其被附加到的所有路由的 HTTP 請(qǐng)求和響應(yīng)。
- 另外,中間件可以終止 HTTP 請(qǐng)求,也可以用 next 將其傳遞給另一個(gè)中間件函數(shù)。中間件的這種“鏈”使你可以對(duì)代碼進(jìn)行劃分并創(chuàng)建可重用的中間件。
編寫 Express 中間件的要求
你需要安裝一些東西來(lái)創(chuàng)建、使用和測(cè)試 Express 中間件。首先需要 Node 和npm。為確保已經(jīng)安裝,可以運(yùn)行:
npm -v && node -v
你應(yīng)該看到已安裝的 Node 和 NPM 版本。如果出現(xiàn)錯(cuò)誤,則需要安裝Node。所有例子都應(yīng)在 Node ver 8+ 和NPM ver 5+ 下使用。
本文使用了 Express 4.x 版。這很重要,因?yàn)閺?3.x 版到 4.x 版有重大的更改。
Express中間件:基礎(chǔ)
首先我們使用 Express 最基本的內(nèi)置中間件。創(chuàng)建一個(gè)新項(xiàng)目并 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);
中間件解決什么問(wèn)題?為什么要用它?
假設(shè)你在 web 網(wǎng)絡(luò)服務(wù)器上正在使用 Node.js和 Express運(yùn)行Web應(yīng)用程序。在此應(yīng)用中,你需要登錄的某些頁(yè)面。
當(dāng) Web 服務(wù)器收到數(shù)據(jù)請(qǐng)求時(shí),Express 將為你提供一個(gè)請(qǐng)求對(duì)象,其中包含有關(guān)用戶及其所請(qǐng)求數(shù)據(jù)的信息。 Express 還使你可以訪問(wèn)響應(yīng)對(duì)象,可以在Web服務(wù)器響應(yīng)用戶之前對(duì)其進(jìn)行修改。這些對(duì)象通??s短為req,res。
中間件函數(shù)是使用相關(guān)信息修改req和res對(duì)象的理想場(chǎng)所。例如用戶登錄后,你可以從數(shù)據(jù)庫(kù)中獲取其用戶詳細(xì)信息,然后將這些詳細(xì)信息存儲(chǔ)在res.user中。
中間件函數(shù)是什么樣的?
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
}
}
如果出現(xiàn)錯(cuò)誤,并且你不想執(zhí)行其他代碼,則不要調(diào)用該函數(shù)。請(qǐng)記住在這種情況下要發(fā)送響應(yīng),否則客戶端將會(huì)等待響應(yīng)直到超時(shí)。
var app = express();
//your normal route Handlers
app.get('/user/:id', userMiddleware, userController);
中間件鏈
你可以在中間件數(shù)組中或著通過(guò)使用多個(gè)app.use調(diào)用來(lái)鏈接中間件:
app.use(middlewareA);
app.use(middlewareB);
app.get('/', [middlewareC, middlewareD], handler);
Express 收到請(qǐng)求后,與請(qǐng)求相匹配的每個(gè)中間件都將會(huì)按照初始化的順序運(yùn)行,直到有終止操作為止。

因此,如果發(fā)生錯(cuò)誤,則將按順序調(diào)用所有用于處理錯(cuò)誤的中間件,直到其中一個(gè)不再調(diào)用next()函數(shù)調(diào)用為止。
Express中間件的類型
- 路由器級(jí)中間件,例如:router.use
- 內(nèi)置中間件,例如:express.static,express.json,express.urlencoded
- 錯(cuò)誤處理中間件,例如:app.use(err,req,res,next)
- 第三方中間件,例如:bodyparser、cookieparser
- 路由器級(jí)中間件
- express.Router 使用 express.Router 類創(chuàng)建模塊化的、可安裝的路由處理。路由實(shí)例是一個(gè)完整的中間件和路由系統(tǒng)。 你可以用中間件進(jìn)行日志記錄、身份驗(yàn)證等操作。如下所示,以記錄用戶的最新活動(dòng)并解析身份驗(yàn)證標(biāo)頭,用它確定當(dāng)前登錄的用戶并將其添加到 Request 對(duì)象。該函數(shù)在程序每次收到請(qǐng)求時(shí)執(zhí)行。如果有錯(cuò)誤,它會(huì)僅結(jié)束響應(yīng),而不會(huì)調(diào)用后續(xù)的中間件或路由處理。
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);
內(nèi)置中間件
Express 有以下內(nèi)置的中間件功能:
- express.static提供靜態(tài)資源,例如html文件,圖像等。
- express.json負(fù)載解析用 JSON 傳入的請(qǐng)求。
- express.urlencoded解析傳入的用 URL 編碼的有效載荷請(qǐng)求。
錯(cuò)誤處理中間件
錯(cuò)誤處理中間件始終采用四個(gè)參數(shù)(err,req,res,next)。你必須通過(guò)提供四個(gè)參數(shù)來(lái)將其標(biāo)識(shí)為錯(cuò)誤處理中間件函數(shù)。即使你不需要使用 next 對(duì)象,也必須指定。否則 next 對(duì)象將被解釋為常規(guī)中間件,并將會(huì)無(wú)法處理錯(cuò)誤?;竞灻缦滤荆?/p>
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);
}
});
在這種情況下,管道末端的錯(cuò)誤處理中間件將會(huì)處理該錯(cuò)誤。你可能還會(huì)注意到,我檢查了res.headersSent屬性。這只是檢查響應(yīng)是否已經(jīng)將標(biāo)頭發(fā)送到客戶端。如果還沒有,它將向客戶端發(fā)送 HTTP 500 狀態(tài)和錯(cuò)誤消息。
例2:
你還可以鏈接錯(cuò)誤處理中間件。通常以不同的方式處理不同類型的錯(cuò)誤:
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)錯(cuò)誤。如果是,它將渲染 “NotFound” 模板頁(yè)面,然后將錯(cuò)誤傳遞到中間件中的下一項(xiàng)。
- 下一個(gè)中間件檢查是否拋出了 304(unauthorized)錯(cuò)誤。如果是,它將渲染“Unauthorized”頁(yè)面,并將錯(cuò)誤傳遞到管道中的下一個(gè)中間件。
- 最后,“catch all” 錯(cuò)誤處理僅記錄錯(cuò)誤,如果未發(fā)送響應(yīng),它將發(fā)送錯(cuò)誤的 httpStatusCode(如果未提供則發(fā)送 HTTP 500 狀態(tài))并渲染 “UnknownError” 模板。
第三方級(jí)別的中間件
在某些情況下,我們將向后端添加一些額外的功能。先安裝 Node.js 模塊獲取所需的功能,然后在應(yīng)用級(jí)別或路由器級(jí)別將其加載到你的應(yīng)用中。
示例:當(dāng) body-parser 處理 Content-Type 請(qǐng)求標(biāo)頭時(shí),所有中間件都將使用解析的正文填充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')
})
總結(jié)
中間件功能是一種非常好的方式,可以對(duì)每個(gè)請(qǐng)求或針對(duì)特定路由的每個(gè)請(qǐng)求運(yùn)行代碼,并對(duì)請(qǐng)求或響應(yīng)數(shù)據(jù)采取措施。中間件是現(xiàn)代 Web 服務(wù)器的重要組成部分,并且非常有用。
以上就是詳解Node.js中間件是怎樣工作的的詳細(xì)內(nèi)容,更多關(guān)于Node.js中間件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
利用nodeJs anywhere搭建本地服務(wù)器環(huán)境的方法
今天小編就為大家分享一篇利用nodeJs anywhere搭建本地服務(wù)器環(huán)境的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
nodejs使用socket5進(jìn)行代理請(qǐng)求的實(shí)現(xiàn)
這篇文章主要介紹了nodejs使用socket5進(jìn)行代理請(qǐng)求的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
詳解node.js搭建代理服務(wù)器請(qǐng)求數(shù)據(jù)
本篇文章主要介紹了詳解node.js搭建代理服務(wù)器請(qǐng)求數(shù)據(jù),具有一定的參考價(jià)值,有興趣的可以了解一下。2017-04-04
VsCode與Node.js知識(shí)點(diǎn)詳解
在本篇文章中小編給大家分享了關(guān)于VsCode與Node.js的相關(guān)知識(shí)點(diǎn)以及安裝等內(nèi)容,需要的朋友們可以參考下。2019-09-09
用nodejs的實(shí)現(xiàn)原理和搭建服務(wù)器(動(dòng)態(tài))
下面小編就為大家?guī)?lái)一篇用nodejs的實(shí)現(xiàn)原理和搭建服務(wù)器(動(dòng)態(tài))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08
node.js中的console.info方法使用說(shuō)明
這篇文章主要介紹了node.js中的console.info方法使用說(shuō)明,本文介紹了console.info的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
手把手教你使用TypeScript開發(fā)Node.js應(yīng)用
為了減少代碼編寫過(guò)程中出現(xiàn)的錯(cuò)誤,以及更好的維護(hù)你的項(xiàng)目,本文將手把手教你配置一個(gè)簡(jiǎn)單的開發(fā)環(huán)境來(lái)編寫Node.js的應(yīng)用程序,感興趣的小伙伴們可以參考一下2019-05-05

