Koa作为下一代Web开发框架,不仅让我们体验到了async/await语法带来同步方式书写异步代码的酸爽,而且本身简洁的特点,更加利于开发者结合业务本身进行扩展。
本文从以下几个方面解读Koa源码:
封装创建应用程序函数 扩展res和req 中间件实现原理 异常处理一、封装创建应用程序函数
利用NodeJS可以很容易编写一个简单的应用程序:
const http = require('http')const server = http.createServer((req, res) => { // 每一次请求处理的方法 console.log(req.url) res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end('Hello NodeJS')})server.listen(8080)
注意:当浏览器发送请求时,会附带请求/favicon.ico。
而Koa在封装创建应用程序的方法中主要执行了以下流程:
组织中间件(监听请求之前) 生成context上下文对象 执行中间件 执行默认响应方法或者异常处理方法// application.jslisten(...args) { const server = http.createServer(this.callback()); return server.listen(...args);}callback() { // 组织中间件 const fn = compose(this.middleware); // 未监听异常处理,则采用默认的异常处理方法 if (!this.listenerCount('error')) this.on('error', this.onerror); const handleRequest = (req, res) => { // 生成context上下文对象 const ctx = this.createContext(req, res); return this.handleRequest(ctx, fn); }; return handleRequest;}handleRequest(ctx, fnMiddleware) { const res = ctx.res; // 默认状态码为404 res.statusCode = 404; // 中间件执行完毕之后 采用默认的 错误 与 成功 的处理方式 const onerror = err => ctx.onerror(err); const handleResponse = () => respond(ctx); onFinished(res, onerror); return fnMiddleware(ctx).then(handleResponse).catch(onerror);}
二、扩展res和req
首先我们要知道NodeJS中的res和req是http.IncomingMessage和http.ServerResponse的实例,那么就可以在NodeJS中这样扩展req和res:
Object.defineProperties(http.IncomingMessage.prototype, { query: { get () { return querystring.parse(url.parse(this.url).query) } }})Object.defineProperties(http.ServerResponse.prototype, { json: { value: function (obj) { if (typeof obj === 'object') { obj = JSON.stringify(obj) } this.end(obj) } }})
而Koa中则是自定义request和response对象,然后保持对res和req的引用,最后通过getter和setter方法实现扩展。
// application.jscreateContext(req, res) { const context = Object.create(this.context); const request = context.request = Object.create(this.request); const response = context.response = Object.create(this.response); context.app = request.app = response.app = this; context.req = request.req = response.req = req; // 保存原生req对象 context.res = request.res = response.res = res; // 保存原生res对象 request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; context.state = {}; // 最终返回完整的context上下文对象 return context;}
新闻热点
疑难解答
图片精选