Koajs中间件之定义(三)

Koajs中间件之定义(一)
Koajs中间件之next(二)
Koajs中间件之context(三)

第一篇文章中我们讲过,“在Koa中,中间件是指连贯整个 Koa 应用程序,并共享资源的独立插件”,注意两个词,“连贯”与“共享资源”,与上面的代码一一对应,“连贯”对应“next”,“共享资源对应context”。
Koa 中通过 context 处理上下文,下面分析一下 context。

context 初始创建

1
2
3
4
5
6
7
8
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.state = {};
return context;
}

不停的创建调用 context 对象,避免对context产生影响,初始 context 对象使用 Object.create() 来克隆对象
context 中包含了几个个主要属性,其中比较重要的有request、response、state。
Koa 中 分别提供 request.js 与 response.js 对原生 req 与 res 对象进行了处理,使得更易操作,例如在request.js 中将 req 原始请求参数由字符串类型替换为对象类型,“?a=1&b=2” <=> {a: 1, b: 2}。

delegates

为了简化对于 context 的操作,Koa中 把 context 中需要调用的方法和属性读取,进行了委托。
有兴趣的可以研究研究 delegates 这个库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')
.method('vary')
.method('set')
.method('append')
.method('flushHeaders')
.access('status')
.access('message')
.access('body')
.access('length')
.access('type')
.access('lastModified')
.access('etag')
.getter('headerSent')
.getter('writable');

例如我们常用的 “ctx.redirect(xxx)” 方法等同于 ctx.reponse.redirect(xxx);
例如输出内容 ctx.body = ‘xxx’ 等同于 ctx.response.body = ‘xxx’;

set & get 处理

对于context中的部分值的设置,进行了 set get 处理,例如 body 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
get body() {
return this._body;
},
set body(val) {
const original = this._body;
this._body = val;

if (this.res.headersSent) return;

// no content
if (val == null) {
if (!statuses.empty[this.status]) this.status = 204;
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.header['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 (typeof val.pipe == 'function') {
onFinish(this.res, destroy.bind(null, val));
ensureErrorHandler(val, this.ctx.onerror);

// overwriting
if (original != null && original != val) this.remove('Content-Length');

if (setType) this.type = 'bin';
return;
}

// json
this.remove('Content-Length');
this.type = 'json';
}

当我们通过 ctx.body 设置输出内容的时候实际上在 Koa 内部进行了一系列处理,例如设置 body 未 null 的情况下,会将 Http 返回状态设置为204。

总体来说,Koa 中关于 context 的源码初看比较复杂,实际上是非常简洁易明的。

参考资料

Mdn 类
Koa 官网