Koa源码学习(2)

context.js

这一部分,定义了Koa的Context对象,其实,这一部分,粗略的看过去,就是把Koa的requestresponse两个对象合到一个context里边,同时加了一些错误处理的便捷方式。

delegation

1
2
3
4
5
6
7
8
9
delegate(proto, 'response')
.method('attachment')
.method('redirect')
...
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
...

最后这一部分代码,就是让ctx可以方便的使用responserequest里的东西,做了一个delegate,本来需要:ctx.response.attachment(),这样只需要:ctx.attachment().

异常/错误处理

1
2
3
4
5
assert: httpAssert, // >>: wrap http-assert into context
throw(...args) { // >>: wrap throw function
throw createError(...args);
},

用的分别是http-asserthttp-errors来进行assert和error处理

this.onerror(error){...}的处理:

```js
// delegate
this.app.emit(‘error’, err, this); // >>: here pass the context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这里发生的错误,也告知给application,这样可以让application做出相应的处理。
这里同时还把`this`传了过去,这样app可以得到context.
```js
let headerSent = false; // >>: already sent
if (this.headerSent || !this.writable) {
headerSent = err.headerSent = true;
}
...
// nothing we can do here other
// than delegate to the app-level
// handler and log.
if (headerSent) {
return;
}

这里判断是不是需要对客户端做出response,如果已经sent了,那么就不用做啥了,否则需要处理response返回给客户端。

后边的代码无非是对不同情况的判断,做出合适的response。

Cookies处理

1
const COOKIES = Symbol('context#cookies');

生成一个唯一的symbol

get和set分别是这样的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

判断如果cookie不存在的话,生成之。

OK,以上就是context的全部内容,主要的工作,还是把requestresponse合成一个。

request.js

700+ line的文件,其实,比较容易看懂,绝大部分的内容,都是对node原有的req的封装和简易处理。

inspect(), toJSON()和之前的没有太大的区别。

大量的内容,都是对原来的req的封装,好奇这里为什么不用delegate直接写完,非要写这么多的代码。

原模原样access的有:

  • header/headers: 请求头
  • url
  • method: 请求方法,比如GET
  • socket

添加处理的:

  • origin: protocol://host
  • href: href
  • host: 获取host
  • hostname: 获取hostname
  • URL: 返回URL对象,lazy生成
  • fresh: last-modified/etag 是否匹配
  • stale: !fresh,已经修改了,需要重新获取
  • idempotent: 看请求方法是不是安全的
  • charset: content-type里的charset
  • length: content-length
  • protocol: 使用的协议,http/https等
  • ips: 当proxy存在的时候,得到ip list
  • ip: remote address
  • subdomains: 获取subdomain数组
  • secure: 判断是不是https
  • path: path
  • query: 路径里的query信息
  • querystring
  • search: querystring带前边的问号

其它一些method/property:

accept, acceptsEncodings, acceptsCharsets, acceptsLanguages:

判断请求是否accept对应的类型,用的accepts

is:

判断请求的content-type是不是自己想要的,用的type-is

type:

返回MIME类型

get:

get对应的header内容,这里还处理了referer的拼写错误,自己也是第一次注意到。

总结来看,request部分做的,就是对于node的req的封装,同时添加了一些helper方法,比如acceptis等。

response.js

不出意外的话,Response也是对Node的res的封装,看一看有什么值得看的地方。

access原来Node的res

  • socket
  • status
  • statusMessage
  • statusMessage=
  • headers
  • length=: content-length
  • headerSent
  • etag=
  • flushHeaders

添加处理的

  • status=: 加入了一些验证,以及对body的处理(如果对应的status body为空,需要设置)
  • length: content-length,如果没有的话,自己需要得到body的长度
  • type=: 设置content-type,有一定判断
  • lastModified: 有一个时间的转换
  • lastModified=
  • etag: 考虑了ETag的两种格式
  • type: content-type,去除了后边诸如charset之类的东西
  • header处理:
    • get: 获取header
    • set: setHeader的封装,支持多种方式
    • append: append header
    • remove: remove header
  • writable: 是否可写,socket.writable,如果已经完成不可写

自己的property/method

body:

操作http的body,需要进行不同情况的处理

  • 内容为null: 204
  • 如果没有设定过status,设为200
  • 没有设置content-type,需要进行设定
  • 对于不同类型的数据:
    • string: 设定length, type
    • buffer: type设为bin
    • stream:
    • json: 设定type,remove content-type

vary:

设置Varyheader

redirect:

重定向!
-如果参数是back: 回到上一页面
-设置status为302
-说明这在redirect

attachment:

当有附加文件的时候,设置Content-Disposition header,同时设置Content-Type,用到了content-disposition

is:

判断type是否满足,类似request

总体来看,responserequest非常相似,封装res,方便处理Node Response的header和body等