application
源码解析
依赖模块
const debug = require('debug')('koa:application')
// 设置名为 koa:application 的 debug 装饰器
const onFinished = require('on-finished')
// 当 HTTP 请求终止时执行回调
const compose = require('koa-compose')
// 合并中间件
const statuses = require('statuses')
// 提供状态码与消息的一一对应关系
const Emitter = require('events')
// 提供事件触发器
const util = require('util')
// 工具模块,支持 Node.js 内部 API 的需求
const Stream = require('stream')
// 工具模块,支持 Node.js 内部 API 的需求
const http = require('http')
// 提供对 HTTP 服务器和客户端的流处理和消息解析
const only = require('only')
// 设置对象白名单属性
const { HttpError } = require('http-errors')
// 创建 HTTP 错误
const context = require('./context')
const request = require('./request')
const response = require('./response')
Application
导出对象
module.exports = class Application extends Emitter {
// 内容如下
}
constructor
constructor (options) {
super()
options = options || {}
this.proxy = options.proxy || false
this.subdomainOffset = options.subdomainOffset || 2
this.proxyIpHeader = options.proxyIpHeader || 'X-Forwarded-For'
this.maxIpsCount = options.maxIpsCount || 0
this.env = options.env || process.env.NODE_ENV || 'development'
if (options.keys) this.keys = options.keys
this.middleware = []
this.context = Object.create(context)
this.request = Object.create(request)
this.response = Object.create(response)
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect
}
}
listen
创建/启动/返回
listen (...args) {
debug('listen')
const server = http.createServer(this.callback())
return server.listen(...args)
}
toJSON
格式化函数
toJSON () {
return only(this, [
'subdomainOffset',
'proxy',
'env'
])
}
inspect
检查函数
inspect () {
return this.toJSON()
}
use
添加中间件
use (fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
debug('use %s', fn._name || fn.name || '-')
this.middleware.push(fn)
return this
}
callback
返回一个原生请求处理回调
callback () {
const fn = compose(this.middleware)
if (!this.listenerCount('error')) this.on('error', this.onerror)
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
return handleRequest
}
handleRequest
处理请求
handleRequest (ctx, fnMiddleware) {
const res = ctx.res
res.statusCode = 404
const onerror = err => ctx.onerror(err)
const handleResponse = () => respond(ctx)
onFinished(res, onerror)
return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}
createContext
创建上下文ctx
createContext (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
context.res = request.res = response.res = res
request.ctx = response.ctx = context
request.response = response
response.request = request
context.originalUrl = request.originalUrl = req.url
context.state = {}
return context
}
onerror
错误处理
onerror (err) {
const isNativeError =
Object.prototype.toString.call(err) === '[object Error]' ||
err instanceof Error
if (!isNativeError) throw new TypeError(util.format('non-error thrown: %j', err))
if (err.status === 404 || err.expose) return
if (this.silent) return
const msg = err.stack || err.toString()
console.error(`\n${msg.replace(/^/gm, ' ')}\n`)
}
default
弥补TS
static get default () {
return Application
}
respond
响应数据处理
function respond (ctx) {
if (ctx.respond === false) return
if (!ctx.writable) return
const res = ctx.res
let body = ctx.body
const code = ctx.status
if (statuses.empty[code]) {
ctx.body = null
return res.end()
}
if (ctx.method === 'HEAD') {
if (!res.headersSent && !ctx.response.has('Content-Length')) {
const { length } = ctx.response
if (Number.isInteger(length)) ctx.length = length
}
return res.end()
}
if (body == null) {
if (ctx.response._explicitNullBody) {
ctx.response.remove('Content-Type')
ctx.response.remove('Transfer-Encoding')
ctx.length = 0
return res.end()
}
if (ctx.req.httpVersionMajor >= 2) {
body = String(code)
} else {
body = ctx.message || String(code)
}
if (!res.headersSent) {
ctx.type = 'text'
ctx.length = Buffer.byteLength(body)
}
return res.end(body)
}
if (Buffer.isBuffer(body)) return res.end(body)
if (typeof body === 'string') return res.end(body)
if (body instanceof Stream) return body.pipe(res)
body = JSON.stringify(body)
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body)
}
res.end(body)
}
HttpError
提供创建 HTTP 错误途径
module.exports.HttpError = HttpError