内容简介:上篇提到,请求到来时,接下来我们看一下上下文 ctx 是怎么创建和使用的。
上篇提到, this.callback()
返回一个回调函数,其实是以闭包的形式返回了一个局部函数变量 handleRequest
,供 Server
调用来处理 HTTP 请求。
callback() {
const fn = compose(this.middleware);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
复制代码
请求到来时, Server
将 Node 提供的原生 request
和 response
传给回调 handleRequest
,它执行两项工作:
-
创建一个上下文
ctx,封装了本次的请求和响应 -
将上下文
ctx和函数fn交由this.handleRequest()处理
接下来我们看一下上下文 ctx 是怎么创建和使用的。
创建上下文 ctx
直接将 Node 提供的原生 request
和 response
传给了 this.createContext()
方法。
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.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
复制代码
代码似乎重复性很大,我们梳理一下:
| 属性 | 含义 |
|---|---|
| context / .ctx | 上下文 |
| req / .req | Node 请求 |
| res / .res | Node 响应 |
| request / .request | Koa 请求 |
| response / .response | Koa响应 |
主要就是上下文、Node 请求&响应、Koa 请求&响应之间的交叉引用,便于使用。
那 ctx
是怎么封装了请求与响应?Node 请求&响应与 Koa 请求&响应之间又是什么关系呢?这就不得不提到 Koa 用到的委托模式了。
委托模式
委托模式(Delegation Pattern)是 设计模式 的一种,意思是外层暴露的对象将请求委托给内部的其他对象进行处理。
从 context.js
可以中看到,Koa 使用 delegates
这个 NPM 包,将本应由上下文 ctx
处理的事情委托给了 request
和 response
,这两个对象来自于 request.js
和 response.js
。
/* context.js */
const delegate = require('delegates');
const proto = module.exports = {
/* 此处是 context 自己完成的一些方法和属性 */
}
/* 委托给 response 处理 */
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.access('status')
.access('body')
.access('length')
/* ... */
/* 委托给 request 处理 */
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.access('method')
.access('query')
.access('path')
.access('url')
.getter('host')
.getter('hostname')
.getter('URL')
/* ... */
复制代码
这样一来,我们对上下文 ctx
的操作,如 ctx.type
和 ctx.length
就会由 response
对象执行, ctx.path
和 ctx.method
就会由 request
对象执行。不要忘了, response
和 request
是 Koa 自己的请求和响应。怎么把它们与 Node 请求&响应联系起来呢?
请求与响应
再啰嗦一遍,真正将请求与响应的操作落实到位的不是上下文 ctx
,而是来自 request.js
的 request
对象和来自 response.js
的 response
对象。我们看一下这两个对象的实现。
/* request.js */
module.exports = {
/* ... */
/**
* Get request URL.
*
* @return {String}
* @api public
*/
get url() {
return this.req.url;
},
/* ... */
}
复制代码
/* response.js */
module.exports = {
/* ... */
/**
* Check if a header has been written to the socket.
*
* @return {Boolean}
* @api public
*/
get headerSent() {
return this.res.headersSent;
},
/* ... */
}
复制代码
原来是靠 Koa 请求/响应去操作 Node 请求/响应来实现的!整个流程串起来就是,上下文 ctx
委托给 Koa 请求/响应,Koa 请求/响应操作 Node 请求/响应,从而实现了完整的请求/响应处理流程。
这个关系弄懂了,Koa的上下文 ctx
是怎么回事也就明白了。
开发中常遇到的获取 POST 参数问题
前面提到, ctx.query
委托给了 request
, request
对 Node 请求中的 query 做了封装,所以我们可以直接用 ctx.query
获取到 GET 参数。
而 POST 请求就没有这种封装,需要通过解析 Node 原生请求来获取其参数。
app.use( async ( ctx ) => {
if ( ctx.url === '/' && ctx.method === 'POST' ) {
// 当 POST 请求的时候,解析 POST 表单里的数据,并显示出来
let postData = await parsePostData( ctx )
ctx.body = postData
}
})
// 解析上下文里 Node 原生请求的 POST 参数
function parsePostData( ctx ) {
return new Promise((resolve, reject) => {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end",function(){
let parseData = parseQueryStr( postdata )
resolve( parseData )
})
} catch ( err ) {
reject(err)
}
})
}
// 将 POST 请求参数字符串解析成 JSON
function parseQueryStr( queryStr ) {
let queryData = {}
let queryStrList = queryStr.split('&')
console.log( queryStrList )
for ( let [ index, queryStr ] of queryStrList.entries() ) {
let itemList = queryStr.split('=')
queryData[ itemList[0] ] = decodeURIComponent(itemList[1])
}
return queryData
}
// 代码来源于:https://chenshenhai.github.io/koa2-note/note/request/post.html
复制代码
也可以直接使用 koa-bodyparser
这个 NPM 包作为中间件完成 POST 数据处理。
const bodyparser = require('koa-bodyparser')
app.use(bodyparser())
app.use( async (ctx) => {
if (ctx.url === '/' && ctx.method === 'POST') {
let data = ctx.request.body
ctx.body = data
}
})
复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- flask 源码解析4:上下文
- Spring源码系列 —— 构造和初始化上下文
- DDD:识别限界上下文以及理解上下文映射
- DDD:识别限界上下文以及理解上下文映射
- 如何划分限界上下文
- 详解Flask上下文
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
锋利的jQuery
单东林、张晓菲、魏然 / 人民邮电出版社 / 2009-6 / 39.00元
《锋利的jQuery》循序渐进地对jQuery的各种函数和方法调用进行了介绍,读者可以系统地掌握jQuery的DOM操作、事件监听和动画、表单操作、AJAX以及插件方面等知识点,并结合每个章节后面的案例演示进行练习,达到掌握核心知识点的目的。为使读者更好地进行开发实践,《锋利的jQuery》的最后一章将前7章讲解的知识点和效果进行了整合,打造出一个非常有个性的网站,并从案例研究、网站材料、网站结构......一起来看看 《锋利的jQuery》 这本书的介绍吧!