NodeJs?Express中間件超詳細講解
什么是中間件
中間件(Middleware ),特指業(yè)務(wù)流程的中間處理環(huán)節(jié)
現(xiàn)實生活中的例子
在處理污水的時候,一般都要經(jīng)過三個處理環(huán)節(jié),從而保證處理過后的廢水,達到排放標準
處理污水的這三個中間處理環(huán)節(jié),就可以叫做中間件。
Express 中間件的調(diào)用流程
當一個請求到達 Express 的服務(wù)器之后,可以連續(xù)調(diào)用多個中間件,從而對這次請求進行預(yù)處理。
Express 中間件的格式
Express 的中間件,本質(zhì)上就是一個 function 處理函數(shù),Express 中間件的格式如下:
注意:中間件函數(shù)的形參列表中,必須包含 next 參數(shù)。而路由處理函數(shù)中只包含 req 和 res。
next 函數(shù)的作用
next 函數(shù)是實現(xiàn)多個中間件連續(xù)調(diào)用的關(guān)鍵,它表示把流轉(zhuǎn)關(guān)系轉(zhuǎn)交給下一個中間件或路由。
定義中間件函數(shù)
可以通過如下的方式,定義一個最簡單的中間件函數(shù):
全局生效的中間件
客戶端發(fā)起的任何請求,到達服務(wù)器之后,都會觸發(fā)的中間件,叫做全局生效的中間件。
通過調(diào)用 app.use(中間件函數(shù)),即可定義一個全局生效的中間件,示例代碼如下:
定義全局中間件的簡化形式
const express = require('express') const app = express() // // 定義一個最簡單的中間件函數(shù) // const mw = function (req, res, next) { // console.log('這是最簡單的中間件函數(shù)') // // 把流轉(zhuǎn)關(guān)系,轉(zhuǎn)交給下一個中間件或路由 // next() // } // // 將 mw 注冊為全局生效的中間件 // app.use(mw) // 這是定義全局中間件的簡化形式 app.use((req, res, next) => { console.log('這是最簡單的中間件函數(shù)') next() }) app.get('/', (req, res) => { console.log('調(diào)用了 / 這個路由') res.send('Home page.') }) app.get('/user', (req, res) => { console.log('調(diào)用了 /user 這個路由') res.send('User page.') }) app.listen(80, () => { console.log('http://127.0.0.1') })
當在apifox請求這兩個請求時,都會先調(diào)用這個中間件再返回請求的內(nèi)容。
中間件的作用
多個中間件之間,共享同一份 req 和 res。基于這樣的特性,我們可以在上游的中間件中,統(tǒng)一為 req 或 res 對象添加自定義的屬性或方法,供下游的中間件或路由進行使用。
const express = require('express') const app = express() // 這是定義全局中間件的簡化形式 app.use((req, res, next) => { // 獲取到請求到達服務(wù)器的時間 const time = Date.now() // 為 req 對象,掛載自定義屬性,從而把時間共享給后面的所有路由 req.startTime = time next() }) app.get('/', (req, res) => { res.send('Home page.' + req.startTime) }) app.get('/user', (req, res) => { res.send('User page.' + req.startTime) }) app.listen(80, () => { console.log('http://127.0.0.1') })
定義多個全局中間件
可以使用 app.use() 連續(xù)定義多個全局中間件??蛻舳苏埱蟮竭_服務(wù)器之后,會按照中間件定義的先后順序依次進行調(diào)用,示例代碼如下:
const express = require('express') const app = express() // 定義第一個全局中間件 app.use((req, res, next) => { console.log('調(diào)用了第1個全局中間件') next() }) // 定義第二個全局中間件 app.use((req, res, next) => { console.log('調(diào)用了第2個全局中間件') next() }) // 定義一個路由 app.get('/user', (req, res) => { res.send('User page.') }) app.listen(80, () => { console.log('http://127.0.0.1') })
局部生效的中間件
不使用 app.use() 定義的中間件,叫做局部生效的中間件,示例代碼如下:
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 1. 定義中間件函數(shù) const mw1 = (req, res, next) => { console.log('調(diào)用了局部生效的中間件') next() } // 2. 創(chuàng)建路由,mw1只在當前路由生效 app.get('/', mw1, (req, res) => { res.send('Home page.') }) app.get('/user', (req, res) => { res.send('User page.') }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
定義多個局部中間件
可以在路由中,通過如下兩種等價的方式,使用多個局部中間件:
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 1. 定義中間件函數(shù) const mw1 = (req, res, next) => { console.log('調(diào)用了第一個局部生效的中間件') next() } const mw2 = (req, res, next) => { console.log('調(diào)用了第二個局部生效的中間件') next() } // 2. 創(chuàng)建路由 app.get('/', [mw1, mw2], (req, res) => { res.send('Home page.') }) app.get('/user', (req, res) => { res.send('User page.') }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
了解中間件的5個使用注意事項
① 一定要在路由之前注冊中間件
② 客戶端發(fā)送過來的請求,可以連續(xù)調(diào)用多個中間件進行處理
③ 執(zhí)行完中間件的業(yè)務(wù)代碼之后,不要忘記調(diào)用 next() 函數(shù)
④ 為了防止代碼邏輯混亂,調(diào)用 next() 函數(shù)后不要再寫額外的代碼
⑤ 連續(xù)調(diào)用多個中間件時,多個中間件之間,共享 req 和 res 對象
中間件的分類
為了方便大家理解和記憶中間件的使用,Express 官方把常見的中間件用法,分成了 5 大類,分別是:
① 應(yīng)用級別的中間件
② 路由級別的中間件
③ 錯誤級別的中間件
④ Express 內(nèi)置的中間件
⑤ 第三方的中間件
應(yīng)用級別的中間件
通過 app.use() 或 app.get() 或 app.post() ,綁定到 app 實例上的中間件,叫做應(yīng)用級別的中間件,代碼示例如下:
路由級別的中間件
綁定到 express.Router() 實例上的中間件,叫做路由級別的中間件。它的用法和應(yīng)用級別中間件沒有任何區(qū)別。只不過,應(yīng)用級別中間件是綁定到 app 實例上,路由級別中間件綁定到 router 實例上,代碼示例如下:
錯誤級別的中間件
錯誤級別中間件的作用:專門用來捕獲整個項目中發(fā)生的異常錯誤,從而防止項目異常崩潰的問題。
格式:錯誤級別中間件的 function 處理函數(shù)中,必須有 4 個形參,形參順序從前到后,分別是 (err, req, res, next)。
注意:錯誤級別的中間件,必須注冊在所有路由之后!
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 1. 定義路由 app.get('/', (req, res) => { // 1.1 人為的制造錯誤 throw new Error('服務(wù)器內(nèi)部發(fā)生了錯誤!') res.send('Home page.') }) // 2. 定義錯誤級別的中間件,捕獲整個項目的異常錯誤,從而防止程序的崩潰 app.use((err, req, res, next) => { console.log('發(fā)生了錯誤!' + err.message) res.send('Error:' + err.message) }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
Express內(nèi)置的中間件
自 Express 4.16.0 版本開始,Express 內(nèi)置了 3 個常用的中間件,極大的提高了 Express 項目的開發(fā)效率和體驗:
① express.static 快速托管靜態(tài)資源的內(nèi)置中間件,例如: HTML 文件、圖片、CSS 樣式等(無兼容性)
② express.json 解析 JSON 格式的請求體數(shù)據(jù)(有兼容性,僅在 4.16.0+ 版本中可用)
③ express.urlencoded 解析 URL-encoded 格式的請求體數(shù)據(jù)(有兼容性,僅在 4.16.0+ 版本中可用)
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 注意:除了錯誤級別的中間件,其他的中間件,必須在路由之前進行配置 // 通過 express.json() 這個中間件,解析表單中的 JSON 格式的數(shù)據(jù) app.use(express.json()) // 通過 express.urlencoded() 這個中間件,來解析 表單中的 url-encoded 格式的數(shù)據(jù) app.use(express.urlencoded({ extended: false })) app.post('/user', (req, res) => { // 在服務(wù)器,可以使用 req.body 這個屬性,來接收客戶端發(fā)送過來的請求體數(shù)據(jù) // 默認情況下,如果不配置解析表單數(shù)據(jù)的中間件,則 req.body 默認等于 undefined console.log(req.body) res.send('ok') }) app.post('/book', (req, res) => { // 在服務(wù)器端,可以通過 req,body 來獲取 JSON 格式的表單數(shù)據(jù)和 url-encoded 格式的數(shù)據(jù) console.log(req.body) res.send('ok') }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
第三方的中間件
非 Express 官方內(nèi)置的,而是由第三方開發(fā)出來的中間件,叫做第三方中間件。在項目中,大家可以按需下載并配置
第三方中間件,從而提高項目的開發(fā)效率。
例如:在 express@4.16.0 之前的版本中,經(jīng)常使用 body-parser 這個第三方中間件,來解析請求體數(shù)據(jù)。使用步
驟如下:
① 運行 npm install body-parser 安裝中間件
② 使用 require 導入中間件
③ 調(diào)用 app.use() 注冊并使用中間件
注意:Express 內(nèi)置的 express.urlencoded 中間件,就是基于 body-parser 這個第三方中間件進一步封裝出來的。
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 1. 導入解析表單數(shù)據(jù)的中間件 body-parser const parser = require('body-parser') // 2. 使用 app.use() 注冊中間件 app.use(parser.urlencoded({ extended: false })) // app.use(express.urlencoded({ extended: false })) app.post('/user', (req, res) => { // 如果沒有配置任何解析表單數(shù)據(jù)的中間件,則 req.body 默認等于 undefined console.log(req.body) res.send('ok') }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
自定義中間件
1. 需求描述與實現(xiàn)步驟
自己手動模擬一個類似于 express.urlencoded 這樣的中間件,來解析 POST 提交到服務(wù)器的表單數(shù)據(jù)。
實現(xiàn)步驟:
① 定義中間件
② 監(jiān)聽 req 的 data 事件
③ 監(jiān)聽 req 的 end 事件
④ 使用 querystring 模塊解析請求體數(shù)據(jù)
⑤ 將解析出來的數(shù)據(jù)對象掛載為 req.body
⑥ 將自定義中間件封裝為模塊
2. 定義中間件
使用 app.use() 來定義全局生效的中間件,代碼如下:
3 .監(jiān)聽req的data事件
在中間件中,需要監(jiān)聽 req 對象的 data 事件,來獲取客戶端發(fā)送到服務(wù)器的數(shù)據(jù)。
如果數(shù)據(jù)量比較大,無法一次性發(fā)送完畢,則客戶端會把數(shù)據(jù)切割后,分批發(fā)送到服務(wù)器。所以 data 事件可能會觸發(fā)多次,每一次觸發(fā) data 事件時,獲取到數(shù)據(jù)只是完整數(shù)據(jù)的一部分,需要手動對接收到的數(shù)據(jù)進行拼接。
4. 監(jiān)聽req的end事件
當請求體數(shù)據(jù)接收完畢之后,會自動觸發(fā) req 的 end 事件。
因此,我們可以在 req 的 end 事件中,拿到并處理完整的請求體數(shù)據(jù)。示例代碼如下:
5. 使用Qs模塊解析請求體數(shù)據(jù)
Node.js 內(nèi)置了一個Qs 模塊,專門用來處理查詢字符串。通過這個模塊提供的 parse() 函數(shù),可以輕松把查詢字符串,解析成對象的格式。示例代碼如下:
6. 將解析出來的數(shù)據(jù)對象掛載為req.body
上游的中間件和下游的中間件及路由之間,共享同一份 req 和 res。因此,我們可以將解析出來的數(shù)據(jù),掛載為 req 的自定義屬性,命名為 req.body,供下游使用。示例代碼如下:
7. 將自定義中間件封裝為模塊
為了優(yōu)化代碼的結(jié)構(gòu),我們可以把自定義的中間件函數(shù),封裝為獨立的模塊,示例代碼如下:
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 導入 Node.js 內(nèi)置的 Qs 模塊 const qs = require('Qs') // 這是解析表單數(shù)據(jù)的中間件 app.use((req, res, next) => { // 定義中間件具體的業(yè)務(wù)邏輯 // 1. 定義一個 str 字符串,專門用來存儲客戶端發(fā)送過來的請求體數(shù)據(jù) let str = '' // 2. 監(jiān)聽 req 的 data 事件 req.on('data', (chunk) => { str += chunk }) // 3. 監(jiān)聽 req 的 end 事件 req.on('end', () => { // 在 str 中存放的是完整的請求體數(shù)據(jù) // console.log(str) // TODO: 把字符串格式的請求體數(shù)據(jù),解析成對象格式 const body = qs.parse(str) req.body = body next() }) }) app.post('/user', (req, res) => { res.send(req.body) }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
封裝自定義中間件
// 導入 Node.js 內(nèi)置的 Qs 模塊 const qs = require('Qs') const bodyParser = (req, res, next) => { // 定義中間件具體的業(yè)務(wù)邏輯 // 1. 定義一個 str 字符串,專門用來存儲客戶端發(fā)送過來的請求體數(shù)據(jù) let str = '' // 2. 監(jiān)聽 req 的 data 事件 req.on('data', (chunk) => { str += chunk }) // 3. 監(jiān)聽 req 的 end 事件 req.on('end', () => { // 在 str 中存放的是完整的請求體數(shù)據(jù) // console.log(str) // TODO: 把字符串格式的請求體數(shù)據(jù),解析成對象格式 const body = qs.parse(str) req.body = body next() }) } module.exports = bodyParser
// 導入 express 模塊 const express = require('express') // 創(chuàng)建 express 的服務(wù)器實例 const app = express() // 1. 導入自己封裝的中間件模塊 const customBodyParser = require('./14.custom-body-parser') // 2. 將自定義的中間件函數(shù),注冊為全局可用的中間件 app.use(customBodyParser) app.post('/user', (req, res) => { res.send(req.body) }) // 調(diào)用 app.listen 方法,指定端口號并啟動web服務(wù)器 app.listen(80, function () { console.log('Express server running at http://127.0.0.1') })
到此這篇關(guān)于NodeJs Express中間件超詳細講解的文章就介紹到這了,更多相關(guān)NodeJs Express中間件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端node Session和JWT鑒權(quán)登錄示例詳解
關(guān)于前端鑒權(quán)登錄是比較常見的需求了,本文將從服務(wù)端渲染和前后端分離的不同角度下演示鑒權(quán),為大家介紹前端node Session和JWT鑒權(quán)登錄示例詳解2022-07-07詳解如何使用Node.js連接數(shù)據(jù)庫ORM
這篇文章主要為大家介紹了詳解如何使用Node.js連接數(shù)據(jù)庫ORM示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12基于docker搭建node環(huán)境開發(fā)服務(wù)器全過程
這篇文章主要給大家介紹了關(guān)于如何基于docker搭建node環(huán)境開發(fā)服務(wù)器的相關(guān)資料,本文將采用docker技術(shù)部署一個簡單的nodejs應(yīng)用,文中通過圖文以及代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11node.js利用socket.io實現(xiàn)多人在線匹配聯(lián)機五子棋
這篇文章主要介紹了node.js利用socket.io實現(xiàn)多人在線匹配聯(lián)機五子棋的操作方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-05-05node.js 使用 net 模塊模擬 websocket 握手進行數(shù)據(jù)傳遞操作示例
這篇文章主要介紹了node.js 使用 net 模塊模擬 websocket 握手進行數(shù)據(jù)傳遞操作,結(jié)合實例形式分析了node.js基于net模塊模擬 websocket握手相關(guān)原理及進行數(shù)據(jù)傳遞具體操作技巧,需要的朋友可以參考下2020-02-02