request
文件源码解析
依赖模块
const URL = require('url').URL
// 处理 url
const net = require('net')
// 模块 net 提供了异步的网络 API
const accepts = require('accepts')
// HTTP 内容协商,可获取网络请求支持的编码、语言、类型等信息
const contentType = require('content-type')
// 创建和解析 Http Content-Type
const stringify = require('url').format
// 序列化 url
const parse = require('parseurl')
// 类似于 url.parse,但是具有缓存功能
const qs = require('querystring')
// 模块 querystring 提供了用于解析和格式化网址查询字符串的实用工具
const typeis = require('type-is')
// 提供对请求的类型检查
const fresh = require('fresh')
// 判断缓存是否过期
const only = require('only')
// 设置对象白名单属性
const util = require('util')
// 工具模块,支持 Node.js 内部 API 的需求
全局变量
const IP = Symbol('context#ip')
// 创建全局唯一变量 IP
导出对象
module.exports = {
// 内容如下
}
header
// 保持 header & headers 一致
get header () {
return this.req.headers
}
set header (val) {
this.req.headers = val
}
get headers () {
return this.req.headers
}
set headers (val) {
this.req.headers = val
}
url
get url () {
return this.req.url
}
set url (val) {
this.req.url = val
}
origin
get origin () {
return `${this.protocol}://${this.host}`
}
href
get href () {
if (/^https?:\/\//i.test(this.originalUrl)) return this.originalUrl
return this.origin + this.originalUrl
}
method
get method () {
return this.req.method
}
set method (val) {
this.req.method = val
}
path
get path () {
return parse(this.req).pathname
}
set path (path) {
const url = parse(this.req)
if (url.pathname === path) return
url.pathname = path
url.path = null
this.url = stringify(url)
}
query
get query () {
const str = this.querystring
const c = this._querycache = this._querycache || {}
return c[str] || (c[str] = qs.parse(str))
}
set query (obj) {
this.querystring = qs.stringify(obj)
}
querystring
get querystring () {
if (!this.req) return ''
return parse(this.req).query || ''
}
set querystring (str) {
const url = parse(this.req)
if (url.search === `?${str}`) return
url.search = str
url.path = null
this.url = stringify(url)
}
search
get search () {
if (!this.querystring) return ''
return `?${this.querystring}`
}
set search (str) {
this.querystring = str
}
host
get host () {
const proxy = this.app.proxy
let host = proxy && this.get('X-Forwarded-Host')
if (!host) {
if (this.req.httpVersionMajor >= 2) host = this.get(':authority')
if (!host) host = this.get('Host')
}
if (!host) return ''
return host.split(/\s*,\s*/, 1)[0]
}
hostname
get hostname () {
const host = this.host
if (!host) return ''
if (host[0] === '[') return this.URL.hostname || ''
return host.split(':', 1)[0]
}
URL
get URL () {
if (!this.memoizedURL) {
const originalUrl = this.originalUrl || ''
try {
this.memoizedURL = new URL(`${this.origin}${originalUrl}`)
} catch (err) {
this.memoizedURL = Object.create(null)
}
}
return this.memoizedURL
}
fresh
get fresh () {
const method = this.method
const s = this.ctx.status
if (method !== 'GET' && method !== 'HEAD') return false
if ((s >= 200 && s < 300) || s === 304) {
return fresh(this.header, this.response.header)
}
return false
}
stale
get stale () {
return !this.fresh
}
idempotent
// 判断请求方法是否幂等
get idempotent () {
const methods = ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']
return !!~methods.indexOf(this.method)
}
socket
// 获取对底层套接字的引用
get socket () {
return this.req.socket
}
charset
get charset () {
try {
const { parameters } = contentType.parse(this.req)
return parameters.charset || ''
} catch (e) {
return ''
}
}
length
get length () {
const len = this.get('Content-Length')
if (len === '') return
return ~~len
}
protocol
get protocol () {
if (this.socket.encrypted) return 'https'
if (!this.app.proxy) return 'http'
const proto = this.get('X-Forwarded-Proto')
return proto ? proto.split(/\s*,\s*/, 1)[0] : 'http'
}
secure
get secure () {
return this.protocol === 'https'
}
ips
get ips () {
const proxy = this.app.proxy
const val = this.get(this.app.proxyIpHeader)
let ips = proxy && val
? val.split(/\s*,\s*/)
: []
if (this.app.maxIpsCount > 0) {
ips = ips.slice(-this.app.maxIpsCount)
}
return ips
}
ip
get ip () {
if (!this[IP]) {
this[IP] = this.ips[0] || this.socket.remoteAddress || ''
}
return this[IP]
}
set ip (_ip) {
this[IP] = _ip
}
subdomains
get subdomains () {
const offset = this.app.subdomainOffset
const hostname = this.hostname
if (net.isIP(hostname)) return []
return hostname
.split('.')
.reverse()
.slice(offset)
}
accept
get accept () {
return this._accept || (this._accept = accepts(this.req))
}
set accept (obj) {
this._accept = obj
}
type
get type () {
const type = this.get('Content-Type')
if (!type) return ''
return type.split(';')[0]
}
^accepts
判断请求支持类型|编码|语言
accepts (...args) {
return this.accept.types(...args)
}
acceptsEncodings (...args) {
return this.accept.encodings(...args)
}
acceptsCharsets (...args) {
return this.accept.charsets(...args)
}
acceptsLanguages (...args) {
return this.accept.languages(...args)
}
is
检查请求类型
is (type, ...types) {
return typeis(this.req, type, ...types)
}
get
获取请求头属性值
get (field) {
const req = this.req
switch (field = field.toLowerCase()) {
case 'referer':
case 'referrer':
return req.headers.referrer || req.headers.referer || ''
default:
return req.headers[field] || ''
}
}
inspect
检查函数
inspect () {
if (!this.req) return
return this.toJSON()
}
toJSON
格式化函数
toJSON () {
return only(this, [
'method',
'url',
'header'
])
}
自定义检查函数
if (util.inspect.custom) {
module.exports[util.inspect.custom] = module.exports.inspect
}