context文件源码解析

依赖模块

  1. const util = require('util')
  2. // 工具模块,支持 Node.js 内部 API 的需求
  3. const createError = require('http-errors')
  4. // 创建 HTTP 错误
  5. const httpAssert = require('http-assert')
  6. // 利用状态码断言
  7. const delegate = require('delegates')
  8. // 委托
  9. const statuses = require('statuses')
  10. // 提供状态码与消息的一一对应关系
  11. const Cookies = require('cookies')
  12. // 创建和设置 HTTP cookies

全局变量

  1. const COOKIES = Symbol('context#cookies')
  2. // 创建全局唯一的变量 COOKIES

proto 导出对象

  1. const proto = module.exports = {
  2. // 内容如下
  3. }

inspect 检查函数

  1. // 检查当前 this 值
  2. inspect () {
  3. if (this === proto) return this
  4. return this.toJSON()
  5. }

toJSON 格式化函数

  1. // 返回 koa 格式化上下文对象
  2. toJSON () {
  3. return {
  4. request: this.request.toJSON(),
  5. response: this.response.toJSON(),
  6. app: this.app.toJSON(),
  7. originalUrl: this.originalUrl,
  8. req: '<original node req>',
  9. res: '<original node res>',
  10. socket: '<original node socket>'
  11. }
  12. }

assert 断言函数

  1. // http-assert 状态码断言方法
  2. assert: httpAssert

throw 抛出错误

  1. // http-errors 抛出用户级 HTTP 错误
  2. throw (...args) {
  3. throw createError(...args)
  4. }

onerror 统一错误处理

  1. // 统一错误处理
  2. onerror (err) {
  3. if (err == null) return
  4. // 判断是否是原生错误,若不是,则创建原生错误
  5. const isNativeError =
  6. Object.prototype.toString.call(err) === '[object Error]' ||
  7. err instanceof Error
  8. if (!isNativeError) err = new Error(util.format('non-error thrown: %j', err))
  9. let headerSent = false
  10. if (this.headerSent || !this.writable) {
  11. headerSent = err.headerSent = true
  12. }
  13. this.app.emit('error', err, this)
  14. if (headerSent) {
  15. return
  16. }
  17. const { res } = this
  18. if (typeof res.getHeaderNames === 'function') {
  19. res.getHeaderNames().forEach(name => res.removeHeader(name))
  20. } else {
  21. res._headers = {}
  22. }
  23. this.set(err.headers)
  24. this.type = 'text'
  25. let statusCode = err.status || err.statusCode
  26. if (err.code === 'ENOENT') statusCode = 404
  27. if (typeof statusCode !== 'number' || !statuses[statusCode]) statusCode = 500
  28. const code = statuses[statusCode]
  29. const msg = err.expose ? err.message : code
  30. this.status = err.status = statusCode
  31. this.length = Buffer.byteLength(msg)
  32. res.end(msg)
  33. }

cookies getter/setter

  1. get cookies () {
  2. if (!this[COOKIES]) {
  3. this[COOKIES] = new Cookies(this.req, this.res, {
  4. keys: this.app.keys,
  5. secure: this.request.secure
  6. })
  7. }
  8. return this[COOKIES]
  9. }
  10. set cookies (_cookies) {
  11. this[COOKIES] = _cookies
  12. }

自定义检查函数

  1. if (util.inspect.custom) {
  2. module.exports[util.inspect.custom] = module.exports.inspect
  3. }

响应委托

  1. // 将 respose 属性方法委托到 proto
  2. delegate(proto, 'response')
  3. .method('attachment')
  4. .method('redirect')
  5. .method('remove')
  6. .method('vary')
  7. .method('has')
  8. .method('set')
  9. .method('append')
  10. .method('flushHeaders')
  11. .access('status')
  12. .access('message')
  13. .access('body')
  14. .access('length')
  15. .access('type')
  16. .access('lastModified')
  17. .access('etag')
  18. .getter('headerSent')
  19. .getter('writable')

请求委托

  1. // 将 requset 属性方法委托到 proto
  2. delegate(proto, 'request')
  3. .method('acceptsLanguages')
  4. .method('acceptsEncodings')
  5. .method('acceptsCharsets')
  6. .method('accepts')
  7. .method('get')
  8. .method('is')
  9. .access('querystring')
  10. .access('idempotent')
  11. .access('socket')
  12. .access('search')
  13. .access('method')
  14. .access('query')
  15. .access('path')
  16. .access('url')
  17. .access('accept')
  18. .getter('origin')
  19. .getter('href')
  20. .getter('subdomains')
  21. .getter('protocol')
  22. .getter('host')
  23. .getter('hostname')
  24. .getter('URL')
  25. .getter('header')
  26. .getter('headers')
  27. .getter('secure')
  28. .getter('stale')
  29. .getter('fresh')
  30. .getter('ips')
  31. .getter('ip')