middleware 中間件
正是因?yàn)橹虚g件的擴(kuò)展性才使得
Koa
的代碼簡(jiǎn)單靈活惨缆。
在 app.js
中宣鄙,有這樣一段代碼:
app.use(async (ctx, next)=>{
await next()
ctx.response.type = 'text/html'
ctx.response.body = '<h1>Hello World</h1>'
})
它的作用是:每收到一個(gè) http
請(qǐng)求,Koa
都會(huì)調(diào)用通過(guò) app.use()
注冊(cè)的 async
函數(shù)医咨,同時(shí)為該函數(shù)傳入 ctx
和 next
兩個(gè)參數(shù)搪搏。而這個(gè) async
函數(shù)就是我們所說(shuō)的中間件。
下面我們簡(jiǎn)單介紹一下傳入中間件的兩個(gè)參數(shù)递览。
ctx
ctx
作為上下文使用叼屠,包含了基本的 ctx.request
和 ctx.response
。另外绞铃,還對(duì) Koa
內(nèi)部對(duì)一些常用的屬性或者方法做了代理操作镜雨,使得我們可以直接通過(guò) ctx
獲取。比如儿捧,ctx.request.url
可以寫(xiě)成 ctx.url
荚坞。
除此之外,Koa
還約定了一個(gè)中間件的存儲(chǔ)空間 ctx.state
菲盾。通過(guò) state
可以存儲(chǔ)一些數(shù)據(jù)西剥,比如用戶數(shù)據(jù),版本信息等亿汞。如果你使用 webpack
打包的話瞭空,可以使用中間件撑刺,將加載資源的方法作為 ctx.state
的屬性傳入到 view
層付翁,方便獲取資源路徑翩腐。
next
next
參數(shù)的作用是將處理的控制權(quán)轉(zhuǎn)交給下一個(gè)中間件量蕊,而 next()
后面的代碼腺晾,將會(huì)在下一個(gè)中間件及后面的中間件(如果有的話)執(zhí)行結(jié)束后再執(zhí)行哨查。
注意: 中間件的順序很重要回俐!
我們重寫(xiě) app.js
來(lái)解釋下中間件的流轉(zhuǎn)過(guò)程:
// 按照官方示例
const Koa = require('koa')
const app = new Koa()
// 記錄執(zhí)行的時(shí)間
app.use(async (ctx, next) => {
let stime = new Date().getTime()
await next()
let etime = new Date().getTime()
ctx.response.type = 'text/html'
ctx.response.body = '<h1>Hello World</h1>'
console.log(`請(qǐng)求地址: ${ctx.path}吹艇,響應(yīng)時(shí)間:${etime - stime}ms`)
});
app.use(async (ctx, next) => {
console.log('中間件1 doSomething')
await next();
console.log('中間件1 end')
})
app.use(async (ctx, next) => {
console.log('中間件2 doSomething')
await next();
console.log('中間件2 end')
})
app.use(async (ctx, next) => {
console.log('中間件3 doSomething')
await next();
console.log('中間件3 end')
})
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
運(yùn)行起來(lái)后麦牺,控制臺(tái)顯示:
server is running at http://localhost:3000
然后打開(kāi)瀏覽器钮蛛,訪問(wèn) http://localhost:3000
,控制臺(tái)顯示內(nèi)容更新為:
server is running at http://localhost:3000
中間件1 doSomething
中間件2 doSomething
中間件3 doSomething
中間件3 end
中間件2 end
中間件1 end
請(qǐng)求地址: /剖膳,響應(yīng)時(shí)間:2ms
從結(jié)果上可以看到魏颓,流程是一層層的打開(kāi),然后一層層的閉合吱晒,像是剝洋蔥一樣 —— 洋蔥模型甸饱。
此外,如果一個(gè)中間件沒(méi)有調(diào)用 await next()
,會(huì)怎樣呢叹话?答案是『后面的中間件將不會(huì)執(zhí)行』偷遗。
修改 app.js
如下,我們?nèi)サ袅说谌齻€(gè)中間件里面的 await
:
// 按照官方示例
const Koa = require('koa')
const app = new Koa()
// 記錄執(zhí)行的時(shí)間
app.use(async (ctx, next) => {
let stime = new Date().getTime()
await next()
let etime = new Date().getTime()
ctx.response.type = 'text/html'
ctx.response.body = '<h1>Hello World</h1>'
console.log(`請(qǐng)求地址: ${ctx.path}驼壶,響應(yīng)時(shí)間:${etime - stime}ms`)
});
app.use(async (ctx, next) => {
console.log('中間件1 doSomething')
await next();
console.log('中間件1 end')
})
app.use(async (ctx, next) => {
console.log('中間件2 doSomething')
// 注意氏豌,這里我們刪掉了 next
// await next()
console.log('中間件2 end')
})
app.use(async (ctx, next) => {
console.log('中間件3 doSomething')
await next();
console.log('中間件3 end')
})
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
重新運(yùn)行代碼后,控制臺(tái)顯示如下:
server is running at http://localhost:3000
中間件1 doSomething
中間件2 doSomething
中間件2 end
中間件1 end
請(qǐng)求地址: /热凹,響應(yīng)時(shí)間:1ms
與我們的預(yù)期結(jié)果『后面的中間件將不會(huì)執(zhí)行』是一致的泵喘。
下一節(jié)中,我們將學(xué)習(xí)下如何響應(yīng)瀏覽器的各種請(qǐng)求碌嘀。