Koa源码阅读(二)上下文ctx

栏目: Node.js · 发布时间: 5年前

内容简介:上篇提到,请求到来时,接下来我们看一下上下文 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 提供的原生 requestresponse 传给回调 handleRequest ,它执行两项工作:

  • 创建一个上下文 ctx ,封装了本次的请求和响应
  • 将上下文 ctx 和函数 fn 交由 this.handleRequest() 处理

接下来我们看一下上下文 ctx 是怎么创建和使用的。

创建上下文 ctx

直接将 Node 提供的原生 requestresponse 传给了 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 处理的事情委托给了 requestresponse ,这两个对象来自于 request.jsresponse.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.typectx.length 就会由 response 对象执行, ctx.pathctx.method 就会由 request 对象执行。不要忘了, responserequest 是 Koa 自己的请求和响应。怎么把它们与 Node 请求&响应联系起来呢?

请求与响应

再啰嗦一遍,真正将请求与响应的操作落实到位的不是上下文 ctx ,而是来自 request.jsrequest 对象和来自 response.jsresponse 对象。我们看一下这两个对象的实现。

/* 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 委托给了 requestrequest 对 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
  }
})
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

水平营销

水平营销

[美] 菲利普·科特勒、费尔南多・德・巴斯 / 陈燕茹 / 中信出版社 / 2005-1 / 25.00元

《水平营销》阐明了相对纵向营销而言的的水平营销的框架和理论。引入横向思维来作为发现新的营销创意的又一平台,旨在获得消费者不可能向营销研究人员要求或建议的点子。而这些点子将帮助企业在产品愈加同质和超竞争的市场中立于不败之地。 《水平营销》提到: 是什么创新过程导致加油站里开起了超市? 是什么创新过程导致取代外卖比萨服务的冷冻比萨的亮相? 是什么创新过程导致巧克力糖里冒出了玩具......一起来看看 《水平营销》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具