资讯专栏INFORMATION COLUMN

玩转Koa -- koa-bodyparser原理解析

andycall / 2648人阅读

摘要:主要通过处理二进制数据流,但是它并不支持字符编码方式,需要通过模块进行处理。最后留图一张往期精彩回顾玩转原理解析玩转核心原理分析

一、前置知识

  在理解koa-bodyparser原理之前,首先需要了解部分HTTP相关的知识。

1、报文主体

  HTTP报文主要分为请求报文和响应报文,koa-bodyparser主要针对请求报文的处理。

  请求报文主要由以下三个部分组成:

报文头部

空行

报文主体

  而koa-bodyparser中的body指的就是请求报文中的报文主体部分。

2、服务器端获取报文主体流程

  HTTP底层采用TCP提供可靠的字节流服务,简单而言就是报文主体部分会被转化为二进制数据在网络中传输,所以服务器端首先需要拿到二进制流数据。

  谈到网络传输,当然会涉及到传输速度的优化,而其中一种优化方式就是对内容进行压缩编码,常用的压缩编码方式有:

gzip

compress

deflate

identity(不执行压缩或不会变化的默认编码格式)

  服务器端会根据报文头部信息中的Content-Encoding确认采用何种解压编码。

  接下来就需要将二进制数据转换为相应的字符,而字符也有不同的字符编码方式,例如对于中文字符处理差异巨大的UTF-8和GBK,UTF-8编码汉字通常需要三个字节,而GBK只需要两个字节。所以还需要在请求报文的头部信息中设置Content-Type使用的字符编码信息(默认情况下采用的是UTF-8),这样服务器端就可以利用相应的字符规则进行解码,得到正确的字符串。

  拿到字符串之后,服务器端又要问了:客户端,你这一段字符串是啥意思啊?

  根据不同的应用场景,客户端会对字符串采用不同的编码方式,常见的编码方式有:

URL编码方式: a=1&b=2

JSON编码方式: {a:1,b:2}

  客户端会将采用的字符串编码方式设置在请求报文头部信息的Content-Type属性中,这样服务器端根据相应的字符串编码规则进行解码,就能够明白客户端所传递的信息了。

  下面一步步分析koa-bodyparser是如何处理这一系列操作,从而得到报文主体内容。

二、获取二进制数据流

  NodeJS中获取请求报文主体二进制数据流主要通过监听request对象的data事件完成:

// 示例一
const http = require("http")

http.createServer((req, res) => {
  const body = []

  req.on("data", chunk => {
    body.push(chunk)
  })
  
  req.on("end", () => {
    const chunks = Buffer.concat(body) // 接收到的二进制数据流

    // 利用res.end进行响应处理
    res.end(chunks.toString())
  })
}).listen(1234)

  而koa-bodyparser主要是对co-body的封装,而【co-body】中主要是采用raw-body模块获取请求报文主体的二进制数据流,【row-body】主要是对上述示例代码的封装和健壮性处理。

三、内容解码

  客户端会将内容编码的方式放入请求报文头部信息Content-Encoding属性中,服务器端接收报文主体的二进制数据了时,会根据该头部信息进行解压操作,当然服务器端可以在响应报文头部信息Accept-Encoding属性中添加支持的解压方式。

  而【row-body】主要采用inflation模块进行解压处理。

四、字符解码

  一般而言,UTF-8是互联网中主流的字符编码方式,前面也提到了还有GBK编码方式,相比较UTF-8,它编码中文只需要2个字节,那么在字符解码时误用UTF-8解码GBK编码的字符,就会出现中文乱码的问题。

  NodeJS主要通过Buffer处理二进制数据流,但是它并不支持GBK字符编码方式,需要通过iconv-lite模块进行处理。

  【示例一】中的代码就存在没有正确处理字符编码的问题,那么报文主体中的字符采用GBK编码方式,必然会出现中文乱码:

const request = require("request")
const iconv = require("iconv-lite")

request.post({
  url: "http://localhost:1234/",
  body: iconv.encode("中文", "gbk"),
  headers: {
    "Content-Type": "text/plain;charset=GBK"
  }
}, (error, response, body) => {
  console.log(body) // 发生中文乱码情况
})

  NodeJS中的Buffer默认是采用UTF-8字符编码处理,这里借助【iconv-lite】模块处理不同的字符编码方式:

    const chunks = Buffer.concat(body)
    res.end(iconv.decode(chunks, charset)) // charset通过Content-Type得到
五、字符串解码

  前面已经提到了字符串的二种编码方式,它们对应的Content-Type分别为:

URL编码 application/x-www-form-urlencoded

JSON编码 application/json

  对于前端来说,URL编码并不陌生,经常会用于URL拼接操作,唯一需要注意的是不要忘记对键值对进行decodeURIComponent()处理。

  当客户端发送请求主体时,需要进行编码操作:

  "a=1&b=2&c=3"

  服务器端再根据URL编码规则解码,得到相应的对象。

  // URL编码方式 简单的解码方法实现
function decode (qs, sep = "&", eq = "=") {
  const obj = {}
  qs = qs.split(sep)

  for (let i = 0, max = qs.length; i < max; i++) {
    const item = qs[i]
    const index = item.indexOf(eq)

    let key, value

    if (~index) {
      key = item.substr(0, index)
      value = item.substr(index + 1)
    } else {
      key = item
      value = ""
    }
    
    key = decodeURIComponent(key)
    value = decodeURIComponent(value)

    if (!obj.hasOwnProperty(key)) {
      obj[key] = value
    }
  }
  return obj
}

console.log(decode("a=1&b=2&c=3")) // { a: "1", b: "2", c: "3" }

  URL编码方式适合处理简单的键值对数据,并且很多框架的Ajax中的Content-Type默认值都是它,但是对于复杂的嵌套对象就不太好处理了,这时就需要JSON编码方式大显身手了。

  客户端发送请求主体时,只需要采用JSON.stringify进行编码。服务器端只需要采用JSON.parse进行解码即可:

const strictJSONReg = /^[x20x09x0ax0d]*([|{)/;
function parse(str) {
  if (!strict) return str ? JSON.parse(str) : str;
  // 严格模式下,总是返回一个对象
  if (!str) return {};
  // 是否为合法的JSON字符串
  if (!strictJSONReg.test(str)) {
    throw new Error("invalid JSON, only supports object and array");
  }
  return JSON.parse(str);
}

  除了上述两种字符串编码方式,koa-bodyparser还支持不采用任何字符串编码方式的普通字符串。

  三种字符串编码的处理方式由【co-body】模块提供,koa-bodyparser中通过判断当前Content-Type类型,调用不同的处理方式,将获取到的结果挂载在ctx.request.body:

  return async function bodyParser(ctx, next) {
    if (ctx.request.body !== undefined) return await next();
    if (ctx.disableBodyParser) return await next();
    try {
      // 最重要的一步 将解析的内容挂载到koa的上下文中
      const res = await parseBody(ctx);
      ctx.request.body = "parsed" in res ? res.parsed : {};
      if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw; // 保存原始字符串
    } catch (err) {
      if (onerror) {
        onerror(err, ctx);
      } else {
        throw err;
      }
    }
    await next();
  };

  async function parseBody(ctx) {
    if (enableJson && ((detectJSON && detectJSON(ctx)) || ctx.request.is(jsonTypes))) {
      return await parse.json(ctx, jsonOpts); // application/json等json type
    }
    if (enableForm && ctx.request.is(formTypes)) {
      return await parse.form(ctx, formOpts); // application/x-www-form-urlencoded
    }
    if (enableText && ctx.request.is(textTypes)) {
      return await parse.text(ctx, textOpts) || ""; // text/plain
    }
    return {};
  }
};

  其实还有一种比较常见的Content-type,当采用表单上传时,报文主体中会包含多个实体主体:

------WebKitFormBoundaryqsAGMB6Us6F7s3SF
Content-Disposition: form-data; name="image"; filename="image.png"
Content-Type: image/png


------WebKitFormBoundaryqsAGMB6Us6F7s3SF
Content-Disposition: form-data; name="text"

------WebKitFormBoundaryqsAGMB6Us6F7s3SF--

  这种方式处理相对比较复杂,koa-bodyparser中并没有提供该Content-Type的解析。(下一篇中应该会介绍^_^)

五、总结

  以上便是koa-bodyparser的核心实现原理,其中涉及到很多关于HTTP的基础知识,对于HTTP不太熟悉的同学,可以推荐看一波入门级宝典【图解HTTP】。

  最后留图一张:

往期精彩回顾

玩转Koa -- koa-router原理解析

玩转Koa -- 核心原理分析

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101073.html

相关文章

  • iKcamp|基于Koa2搭建Node.js实战(含视频)☞ HTTP请求

    POST/GET请求——常见请求方式处理 ?? iKcamp 制作团队 原创作者:大哼、阿干、三三、小虎、胖子、小哈、DDU、可木、晃晃 文案校对:李益、大力萌、Au、DDU、小溪里、小哈 风采主播:可木、阿干、Au、DDU、小哈 视频剪辑:小溪里 主站运营:给力xi、xty 教程主编:张利涛 视频地址:https://www.cctalk.com/v/15114357765870 ...

    张利勇 评论0 收藏0
  • 从零开始做Vue前端架构(4)

    摘要:前言上一篇我们遇到少年,是不是忘了的警告,这一篇我们就来解决这个问题。总结通过实现前后端分离,并且使用来更方便的模拟数据。下一篇,我们创建发布环境下的配置文件,并且看看怎么优化产出的代码的从零开始做前端架构项目完整代码前端架构子咻 前言 上一篇我们遇到少年,是不是忘了npm run mock?的警告,这一篇我们就来解决这个问题。 开发 一、安装包 安装koa和一系列的包(我们用的是ko...

    xuweijian 评论0 收藏0
  • vue+koa2+token登陆验证

    摘要:用搭建前端项目用搭建后台,给前端提供数据访问接口项目结构用搭建的项目,红色框中是新建的文件夹用于存放剩下的文件在写项目中慢慢增加,最初就是这样的之后将项目跑起来,看一下有没有问题这里就当作没有问题前端这里选用和搭配这里采用的是的完整 koa2+vue 用vue-cli搭建前端项目 用koa2搭建后台,给前端提供数据访问接口 项目结构 showImg(https://segmentf...

    econi 评论0 收藏0
  • 玩转Koa -- koa-router原理解析

    摘要:四路由注册构造函数首先看了解一下构造函数限制必须采用关键字服务器支持的请求方法,后续方法会用到保存前置处理函数存储在构造函数中初始化的和属性最为重要,前者用来保存前置处理函数,后者用来保存实例化的对象。 一、前言   Koa为了保持自身的简洁,并没有捆绑中间件。但是在实际的开发中,我们需要和形形色色的中间件打交道,本文将要分析的是经常用到的路由中间件 -- koa-router。   ...

    wthee 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<