文中提到的koa均為koa2
提到nodejs, 想必大家都知道express和koa.
express: 大
koa: 小
比較的的是功能, 社區, 中間件,相關資源等
這里我就專門說說中間件吧, 很多人可能說express插件豐富啊. 其實除了中間件, 其余什么的和express koa本身並沒有多大關系, 不都是基於nodejs http(https)的封裝嘛.
express中間件是非常的豐富, 但是提個醒, express的中間件是可以跑在koa下面的.到這里就要提到 koa-connect.
那么,我們就來簡單看看這個koa-connect是怎么實現的.
其源碼一共才38行, 一半多注釋, 3個方法.
- koaConnect: 對外公布的方法, 對express的中間件的參數進行分析,分別調用noCallbackHandler和withCallbackHandler
- noCallbackHandler : 處理無回調的express的中間件
- withCallbackHandler : 處理有回調的express的中間件
這里的回調就是有無next方法, next方法就是進入下一個中間件
/**
* If the middleware function does declare receiving the `next` callback
* assume that it's synchronous and invoke `next` ourselves
*/
function noCallbackHandler(ctx, connectMiddleware, next) {
connectMiddleware(ctx.req, ctx.res)
return next()
}
/**
* The middleware function does include the `next` callback so only resolve
* the Promise when it's called. If it's never called, the middleware stack
* completion will stall
*/
function withCallbackHandler(ctx, connectMiddleware, next) {
return new Promise((resolve, reject) => {
connectMiddleware(ctx.req, ctx.res, err => {
if (err) reject(err)
else resolve(next())
})
})
}
/**
* Returns a Koa middleware function that varies its async logic based on if the
* given middleware function declares at least 3 parameters, i.e. includes
* the `next` callback function
*/
function koaConnect(connectMiddleware) {
const handler = connectMiddleware.length < 3
? noCallbackHandler
: withCallbackHandler
return function koaConnect(ctx, next) {
return handler(ctx, connectMiddleware, next)
}
}
module.exports = koaConnect
koaConnect方法
function koaConnect(connectMiddleware) {
const handler = connectMiddleware.length < 3
? noCallbackHandler
: withCallbackHandler
return function koaConnect(ctx, next) {
return handler(ctx, connectMiddleware, next)
}
}
kaoConnect返回的是一個koa版本的中間件.
connectMiddleware.length是express中間件參數的長度, 如果你這個中間件使用了arguments進行參數解析或者使用了rest參數, 那么這個length本身就准確了.
通過express中間件參數的長度分別調用有回調和無回調的方法.
noCallbackHandler方法
function noCallbackHandler(ctx, connectMiddleware, next) {
connectMiddleware(ctx.req, ctx.res)
return next()
}
直接調用express方法, ctx.req 和 ctx.res作為參數傳入.
ctx.req: Node 的 request 對象.
ctx.res: Node 的 Reponse 對象.
這說明express的中間的req 和 res 和koa中間件的ctx.req 和res就是一個玩意.
因為express中間件沒有調用next, 所以被調用完畢, 直接調用koa中間件的next
withCallbackHandler方法
function withCallbackHandler(ctx, connectMiddleware, next) {
return new Promise((resolve, reject) => {
connectMiddleware(ctx.req, ctx.res, err => {
if (err) reject(err)
else resolve(next())
})
})
}
返回一個Promise, 因為koa中有 await next()的形式調用, 這就很好的滿足了需求.
express中間里面假如有第三個參數next,調用表示進入下一個中間件.
express中間件next調用的時候可以傳入Error,
可以參考express error handling
app.get("/", function (req, res, next) {
fs.readFile("/file-does-not-exist", function (err, data) {
if (err) {
next(err); // Pass errors to Express.
}
else {
res.send(data);
}
});
});
回到我們的withCallbackHandler方法
這里express的中間件的next方法即為
err => {
if (err) reject(err)
else resolve(next())
}
當有錯誤的時候,reject
當沒有錯誤的時候, 調用koa中間件的next, 繼續下面的執行.
koa-connect就分析完畢了, 核心就兩點
- 通過express中間件的參數長度來區分處理
- 改造express中間件的next方法