context
文件源码解析
依赖模块
const util = require('util')
// 工具模块,支持 Node.js 内部 API 的需求
const createError = require('http-errors')
// 创建 HTTP 错误
const httpAssert = require('http-assert')
// 利用状态码断言
const delegate = require('delegates')
// 委托
const statuses = require('statuses')
// 提供状态码与消息的一一对应关系
const Cookies = require('cookies')
// 创建和设置 HTTP cookies
全局变量
const COOKIES = Symbol('context#cookies')
// 创建全局唯一的变量 COOKIES
proto
导出对象
const proto = module.exports = {
// 内容如下
}
inspect
检查函数
// 检查当前 this 值
inspect () {
if (this === proto) return this
return this.toJSON()
}
toJSON
格式化函数
// 返回 koa 格式化上下文对象
toJSON () {
return {
request: this.request.toJSON(),
response: this.response.toJSON(),
app: this.app.toJSON(),
originalUrl: this.originalUrl,
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>'
}
}
assert
断言函数
// http-assert 状态码断言方法
assert: httpAssert
throw
抛出错误
// http-errors 抛出用户级 HTTP 错误
throw (...args) {
throw createError(...args)
}
onerror
统一错误处理
// 统一错误处理
onerror (err) {
if (err == null) return
// 判断是否是原生错误,若不是,则创建原生错误
const isNativeError =
Object.prototype.toString.call(err) === '[object Error]' ||
err instanceof Error
if (!isNativeError) err = new Error(util.format('non-error thrown: %j', err))
let headerSent = false
if (this.headerSent || !this.writable) {
headerSent = err.headerSent = true
}
this.app.emit('error', err, this)
if (headerSent) {
return
}
const { res } = this
if (typeof res.getHeaderNames === 'function') {
res.getHeaderNames().forEach(name => res.removeHeader(name))
} else {
res._headers = {}
}
this.set(err.headers)
this.type = 'text'
let statusCode = err.status || err.statusCode
if (err.code === 'ENOENT') statusCode = 404
if (typeof statusCode !== 'number' || !statuses[statusCode]) statusCode = 500
const code = statuses[statusCode]
const msg = err.expose ? err.message : code
this.status = err.status = statusCode
this.length = Buffer.byteLength(msg)
res.end(msg)
}
cookies
getter/setter
get cookies () {
if (!this[COOKIES]) {
this[COOKIES] = new Cookies(this.req, this.res, {
keys: this.app.keys,
secure: this.request.secure
})
}
return this[COOKIES]
}
set cookies (_cookies) {
this[COOKIES] = _cookies
}
自定义检查函数
if (util.inspect.custom) {
module.exports[util.inspect.custom] = module.exports.inspect
}
响应委托
// 将 respose 属性方法委托到 proto
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('has')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable')
请求委托
// 将 requset 属性方法委托到 proto
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.method('acceptsCharsets')
.method('accepts')
.method('get')
.method('is')
.access('querystring')
.access('idempotent')
.access('socket')
.access('search')
.access('method')
.access('query')
.access('path')
.access('url')
.access('accept')
.getter('origin')
.getter('href')
.getter('subdomains')
.getter('protocol')
.getter('host')
.getter('hostname')
.getter('URL')
.getter('header')
.getter('headers')
.getter('secure')
.getter('stale')
.getter('fresh')
.getter('ips')
.getter('ip')