nodejs的网络请求解析
Posted Mr song song
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nodejs的网络请求解析相关的知识,希望对你有一定的参考价值。
一、context.js
在context.js中,会处理错误,cookie,JSON格式化等。
1)cookie
在处理cookie时,创建了一个Symbol类型的key,注意Symbol具有唯一性的特点,即 Symbol('context#cookies') === Symbol('context#cookies') 得到的是 false。
const Cookies = require('cookies')
const COOKIES = Symbol('context#cookies')
const proto = module.exports =
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
get在读取cookie,会初始化Cookies实例。
2)错误
在默认的错误处理函数中,会配置头信息,触发错误事件,配置响应码等。
onerror (err)
// 可以绕过KOA的错误处理
if (err == null) return
// 在处理跨全局变量时,正常的“instanceof”检查无法正常工作
// See https://github.com/koajs/koa/issues/1466
// 一旦jest修复,可能会删除它 https://github.com/facebook/jest/issues/2549.
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
// 触发error事件,在application.js中创建过监听器
this.app.emit('error', err, this)
// nothing we can do here other
// than delegate to the app-level
// handler and log.
if (headerSent)
return
const res = this
// 首先取消设置所有header
/* istanbul ignore else */
if (typeof res.getHeaderNames === 'function')
res.getHeaderNames().forEach(name => res.removeHeader(name))
else
res._headers = // Node < 7.7
// 然后设置那些指定的
this.set(err.headers)
// 强制 text/plain
this.type = 'text'
let statusCode = err.status || err.statusCode
// ENOENT support
if (err.code === 'ENOENT') statusCode = 404
// default to 500
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)
,
3)属性委托
在package.json中依赖了一个名为delegates的库,看下面这个示例。
request是context的一个属性,在调delegate(context, 'request')函数后,就能直接context.querystring这么调用了。
const delegate = require('delegates')
const context =
request:
querystring: 'a=1&b=2'
delegate(context, 'request')
.access('querystring')
console.log(context.querystring)// a=1&b=2
在KOA中,ctx可以直接调用request与response的属性和方法就是通过delegates实现的。
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')
...
二、request.js和response.js
request.js和response.js就是为Node原生的req和res做一层封装。在request.js中都是些HTTP首部、IP、URL、缓存等。
/**
* 获取 WHATWG 解析的 URL,并缓存起来
*/
get URL ()
/* istanbul ignore else */
if (!this.memoizedURL)
const originalUrl = this.originalUrl || '' // avoid undefined in template string
try
this.memoizedURL = new URL(`$this.origin$originalUrl`)
catch (err)
this.memoizedURL = Object.create(null)
return this.memoizedURL
,
/**
* 检查请求是否新鲜(有缓存),也就是
* If-Modified-Since/Last-Modified 和 If-None-Match/ETag 是否仍然匹配
*/
get fresh ()
const method = this.method
const s = this.ctx.status
// GET or HEAD for weak freshness validation only
if (method !== 'GET' && method !== 'HEAD') return false
// 2xx or 304 as per rfc2616 14.26
if ((s >= 200 && s < 300) || s === 304)
return fresh(this.header, this.response.header)
return false
,
response.js要复杂一点,会配置状态码、响应正文、读取解析的响应内容长度、302重定向等。
/**
* 302 重定向
* Examples:
* this.redirect('back');
* this.redirect('back', '/index.html');
* this.redirect('/login');
* this.redirect('http://google.com');
*/
redirect (url, alt)
// location
if (url === 'back') url = this.ctx.get('Referrer') || alt || '/'
this.set('Location', encodeUrl(url))
// status
if (!statuses.redirect[this.status]) this.status = 302
// html
if (this.ctx.accepts('html'))
url = escape(url)
this.type = 'text/html; charset=utf-8'
this.body = `Redirecting to <a href="$url">$url</a>.`
return
// text
this.type = 'text/plain; charset=utf-8'
this.body = `Redirecting to $url.`
,
/**
* 设置响应正文
*/
set body (val)
const original = this._body
this._body = val
// no content
if (val == null)
if (!statuses.empty[this.status])
if (this.type === 'application/json')
this._body = 'null'
return
this.status = 204
if (val === null) this._explicitNullBody = true
this.remove('Content-Type')
this.remove('Content-Length')
this.remove('Transfer-Encoding')
return
// set the status
if (!this._explicitStatus) this.status = 200
// set the content-type only if not yet set
const setType = !this.has('Content-Type')
// string
if (typeof val === 'string')
if (setType) this.type = /^\\s*</.test(val) ? 'html' : 'text'
this.length = Buffer.byteLength(val)
return
// buffer
if (Buffer.isBuffer(val))
if (setType) this.type = 'bin'
this.length = val.length
return
// stream
if (val instanceof Stream)
onFinish(this.res, destroy.bind(null, val))
if (original !== val)
val.once('error', err => this.ctx.onerror(err))
// overwriting
if (original != null) this.remove('Content-Length')
if (setType) this.type = 'bin'
return
// json
this.remove('Content-Length')
this.type = 'json'
,
三、单元测试
KOA的单元测试做的很细致,每个方法和属性都给出了相应的单测,它内部的写法很容易单测,非常值得借鉴。
采用的单元测试框架是Jest,HTTP请求的测试库是SuperTest。断言使用了Node默认提供的assert模块。
const request = require('supertest')
const assert = require('assert')
const Koa = require('../..')
// request.js
describe('app.request', () =>
const app1 = new Koa()
// 声明request的message属性
app1.request.message = 'hello'
it('should merge properties', () =>
app1.use((ctx, next) =>
// 判断ctx中的request.message是否就是刚刚赋的那个值
assert.strictEqual(ctx.request.message, 'hello')
ctx.status = 204
)
// 发起GET请求,地址是首页,期待响应码是204
return request(app1.listen())
.get('/')
.expect(204)
)
)
以上是关于nodejs的网络请求解析的主要内容,如果未能解决你的问题,请参考以下文章