在nodejs中,中間件主要是指封裝所有Http請求細節處理的方法,是從Http請求發起到回應結束過程中的處理方法。中間件的行為比較類似Java中過濾器的工作原理,就是在進入具體的業務處理之前,先讓過濾器處理。
本教學操作環境:windows7系統、nodejs 12.19.0版、Dell G3電腦。
在NodeJS中,中間件主要是指封裝所有Http請求細節處理的方法。一次Http請求通常包含很多工作,如記錄日誌、ip過濾、查詢字串、請求體解析、Cookie處理、權限驗證、參數驗證、異常處理等,但對於Web應用而言,並不希望接觸到這麼多細節性的處理,因此引入中間件來簡化和隔離這些基礎設施與業務邏輯之間的細節,讓開發者能夠專注於在業務的開發上,以達到提升開發效率的目的。
中間件的行為比較類似Java中過濾器的工作原理,就是在進入具體的業務處理之前,先讓過濾器處理。它的工作模型下圖所示。
中間件是從Http請求發起到在回應結束過程中的處理方法,通常需要對請求和回應進行處理,因此一個基本的中間件的形式如下:
const middleware = (req, res, next) => { // TODO next() }
以下透過兩種方式的中間件機制的實作來理解中間件是如何工作的。
方式一
如下定義三個簡單的中間件:
const middleware1 = (req, res, next) => { console.log('middleware1 start') next() } const middleware2 = (req, res, next) => { console.log('middleware2 start') next() } const middleware3 = (req, res, next) => { console.log('middleware3 start') next() }
// 中间件数组 const middlewares = [middleware1, middleware2, middleware3] function run (req, res) { const next = () => { // 获取中间件数组中第一个中间件 const middleware = middlewares.shift() if (middleware) { middleware(req, res, next) } } next() } run() // 模拟一次请求发起
執行以上程式碼,可以看到如下結果:
middleware1 start middleware2 start middleware3 start
如果中間件中有非同步操作,需要在非同步操作的流程結束後再呼叫next()
方法,否則中間件不能依序執行。改寫middleware2中介軟體:
const middleware2 = (req, res, next) => { console.log('middleware2 start') new Promise(resolve => { setTimeout(() => resolve(), 1000) }).then(() => { next() }) }
執行結果與前一個一致,不過middleware3會在middleware2非同步完成後執行。
middleware1 start middleware2 start middleware3 start
有些中間件不只需要在業務處理前執行,還需要在業務處理後執行,例如統計時間的日誌中間件。在方式一情況下,無法在next()
為非同步操作時再將目前中間件的其他程式碼作為回呼執行。因此可以將next()
方法的後續操作封裝成一個Promise
對象,中間件內部就可以使用next.then()
形式完成業務處理結束後的回調。改寫run()
方法如下:
function run (req, res) { const next = () => { const middleware = middlewares.shift() if (middleware) { // 将middleware(req, res, next)包装为Promise对象 return Promise.resolve(middleware(req, res, next)) } } next() }
中間件的呼叫方式需改寫為:
const middleware1 = (req, res, next) => { console.log('middleware1 start') // 所有的中间件都应返回一个Promise对象 // Promise.resolve()方法接收中间件返回的Promise对象,供下层中间件异步控制 return next().then(() => { console.log('middleware1 end') }) }
const middleware1 = (req, res, next) => { console.log('middleware1 start') // 所有的中间件都应返回一个Promise对象 // Promise.resolve()方法接收中间件返回的Promise对象,供下层中间件异步控制 return next().then((res) => { console.log("1",res) return 'middleware1 end'; }) } const middleware2 = (req, res, next) => { console.log('middleware2 start') // 所有的中间件都应返回一个Promise对象 // Promise.resolve()方法接收中间件返回的Promise对象,供下层中间件异步控制 // console.log("next()",next()) return next().then((res) => { console.log("2",res) return 'middleware2 end' }) } const middleware3 = (req, res, next) => { console.log('middleware3 start') return next().then((res) => { console.log("3",res) return 'middleware3 end' }) } const middlewares = [middleware1, middleware2, middleware3]function run (req, res) { const next = () => { const middleware = middlewares.shift() if (middleware) { // console.log("next",next) // 将middleware(req, res, next)包装为Promise对象 return Promise.resolve(middleware(req, res, next)) }else { return Promise.resolve("结束"); } } next() } run() // 模拟一次请求发起
結果:
async await 實作
const middleware1 = async (req, res, next) => { console.log('middleware1 start') let result = await next(); console.log("1",result) } const middleware2 = async (req, res, next) => { console.log('middleware2 start') let result = await next(); console.log("2",result) return 'middleware2 end'; } const middleware3 = async (req, res, next) => { console.log('middleware3 start') let result = await next(); console.log("3",result) return 'middleware3 end'; } const middlewares = [middleware1, middleware2, middleware3] function run (req, res) { const next = () => { const middleware = middlewares.shift() if (middleware) { // console.log("next",next) // 将middleware(req, res, next)包装为Promise对象 return Promise.resolve(middleware(req, res, next)) }else { return Promise.resolve("结束"); } } next() } run() // 模拟一次请求发起
#以上描述了中間件機制中多個非同步中間件的呼叫流程,實際中間件機制的實作還需要考慮異常處理、路由等。
在express
框架中,中間件的實作方式為方式一,且全域中間件和內建路由中間件中根據請求路徑定義的中間件共同作用,不過無法在業務處理結束後再呼叫目前中間件中的程式碼。 koa2
框架中中介軟體的實作方式為方式二,將next()
方法傳回值封裝成一個Promise
,以便後續中間件的非同步流程控制,實現了koa2
框架提出的洋蔥圈模型,即每一層中間件相當於一個球面,當貫穿整個模型時,實際上每個球面會穿透兩次。
#koa2
框架的中間件機制實現得非常簡潔優雅,這裡學習框架中組合多個中間件的核心代碼。
function compose (middleware) { if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!') for (const fn of middleware) { if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!') } return function (context, next) { let index = -1 return dispatch(0) function dispatch (i) { // index会在next()方法调用后累加,防止next()方法重复调用 if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i let fn = middleware[i] if (i === middleware.length) fn = next if (!fn) return Promise.resolve() try { // 核心代码 // 包装next()方法返回值为Promise对象 return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); } catch (err) { // 遇到异常中断后续中间件的调用 return Promise.reject(err) } } } }
本文主要介紹了中間件的概念、為何引入中間件以及中介軟體機制的核心實作。中介軟體機制使得Web應用具備良好的可擴充性和組合性。
在實作中間件時,單一中間件應該夠簡單,職責單一。由於每個請求都會呼叫中間件相關代碼,中間件的代碼應該高效,必要的時候可以快取重複獲取的資料。在對不同的路由使用中間件時,也應該考慮到不同的中間件應用到不同的路由。
【推薦學習:《nodejs 教學》】
以上是nodejs中的中間件是什麼意思的詳細內容。更多資訊請關注PHP中文網其他相關文章!