用法
const Koa = require("koa")const app = new Koa()//middlewareapp.use(async (ctx, next) => {const start = Date.now()await next()const ms = Date.now() - startconsole.log(`${ctx.method} ${ctx.url} - ${ms}ms`)})// responseapp.use(ctx => {ctx.body = "Hello Koa"})app.listen(3000)
创建应用程序
function listen() {const server = http.createServer(callback())return server.listen()}function callback() {// 组织中间件const fn = compose(middleware)// 监听异常处理on("error", onerror)// 每次请求调用该函数const handleRequest = (req, res) => {// 接收两个参数 原始node的 req,res 对象,http上的类生成的实例// 生成上下文对象const ctx = createContext(req, res)return handleRequest(ctx, fn)}return handleRequest}function handleRequest(ctx, fnMiddleware) {const res = ctx.res// 默认状态码为404res.statusCode = 404// 中间件执行完毕之后 采用默认的 错误 与 成功 的处理方式const onerror = err => ctx.onerror(err)const handleResponse = () => respond(ctx)onFinished(res, onerror)return fnMiddleware(ctx).then(handleResponse).catch(onerror)}
生成上下文对象
function createContext(req, res) {// this.context 会定义属性代理、方法代理// 属性代理 access (context, response|request, header) Object.defineProperty// 方法代理 method (context, response|request, send) 通过返回新函数const context = Object.create(this.context)// 自定义request、response对象const request = (context.request = Object.create(this.request))const response = (context.response = Object.create(this.response))context.app = request.app = response.app = thiscontext.req = request.req = response.req = reqcontext.res = request.res = response.res = resrequest.ctx = response.ctx = contextrequest.response = responseresponse.request = requestcontext.originalUrl = request.originalUrl = req.urlcontext.state = {}return context}
中间件实现原理
定义compose函数,返回的函数每次请求都会被调用,注册的中间件每一次请求都会执行
function compose(middleware) {
return function(context) {
return dispatch(0)
// 返回函数内部定义next函数,该函数接收自增的标识符,每次执行加1,初始为0,通过该参数取出队列中的中间件函数执行,
// 并且把上下文对象以及next函数自身传给中间件函数,这样中间件函数就可以实现业务逻辑,并且主动调用next函数,消费队列里的中间件,这样就实行了中间件
function dispatch(i) {
// 取出中间件
let fn = middleware[i]
if (!fn) return Promise.resolve()
try {
// 递归调用下一个中间件
return Promise.resolve(fn(context, () => dispatch(i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}
