地址:http://www.imooc.com/article/274059

一、前置知识

  在理解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()

而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 = , max = qs.length; i < max; i++) {
const item = qs[i]
const index = item.indexOf(eq) let key, value if (~index) {
key = item.substr(, index)
value = item.substr(index + )
} 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 = /^[\x20\x09\x0a\x0d]*(\[|\{)/;
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】。

 

作者:descire
链接:http://www.imooc.com/article/274059
来源:慕课网
本文原创发布于慕课网 ,转载请注明出处,谢谢合作

(转)玩转Koa -- koa-bodyparser原理解析的更多相关文章

  1. 深入解析Koa之核心原理

    这篇文章主要介绍了玩转Koa之核心原理分析,本文从封装创建应用程序函数.扩展res和req.中间件实现原理.异常处理的等这几个方面来介绍,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参 ...

  2. [原][Docker]特性与原理解析

    Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...

  3. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

  4. Web APi之过滤器执行过程原理解析【二】(十一)

    前言 上一节我们详细讲解了过滤器的创建过程以及粗略的介绍了五种过滤器,用此五种过滤器对实现对执行Action方法各个时期的拦截非常重要.这一节我们简单将讲述在Action方法上.控制器上.全局上以及授 ...

  5. Web APi之过滤器创建过程原理解析【一】(十)

    前言 Web API的简单流程就是从请求到执行到Action并最终作出响应,但是在这个过程有一把[筛子],那就是过滤器Filter,在从请求到Action这整个流程中使用Filter来进行相应的处理从 ...

  6. GeoHash原理解析

    GeoHash 核心原理解析       引子 一提到索引,大家脑子里马上浮现出B树索引,因为大量的数据库(如MySQL.oracle.PostgreSQL等)都在使用B树.B树索引本质上是对索引字段 ...

  7. alibaba-dexposed 原理解析

    alibaba-dexposed 原理解析 使用参考地址: http://blog.csdn.net/qxs965266509/article/details/49821413 原理参考地址: htt ...

  8. 支付宝Andfix 原理解析

    支付宝Andfix 原理解析 使用参考地址: http://blog.csdn.net/qxs965266509/article/details/49802429 原理参考地址: http://blo ...

  9. JavaScript 模板引擎实现原理解析

    1.入门实例 首先我们来看一个简单模板: <script type="template" id="template"> <h2> < ...

  10. Request 接收参数乱码原理解析三:实例分析

    通过前面两篇<Request 接收参数乱码原理解析一:服务器端解码原理>和<Request 接收参数乱码原理解析二:浏览器端编码原理>,了解了服务器和浏览器编码解码的原理,接下 ...

随机推荐

  1. JavaWeb学习——Servlet相关的接口和类

    JavaWeb学习——Servlet相关的接口和类 摘要:本文主要学习了Servlet相关的接口和类. Servlet的接口和类 三种方式 实现Servlet有三种方式: 实现javax.servle ...

  2. 番茄助手 最新 Visual Assist X 适应于VS2019 VS2017 VS2015 VS2013 亲测可用

    番茄助手 最新 Visual Assist X 适应于VS2019 VS2017 VS2015 VS2013 亲测可用 如图: 颜色已经改变: 下载说明: /* INSTALLATION 0) Uni ...

  3. 再看Java之温故知新(体系篇)

    一 数据类型 1.1 8种基本数据类型 1.2 引用数据类型 1.3 java内存机制 1.3.1 寄存器 1.3.2 栈 1.3.3 堆 1.3.4. 静态区/方法区 1.3.5. 运行时常量池(R ...

  4. 使用动态SQL处理table_name作为输入参数的存储过程(MySQL)

    关于mysql如何创建和使用存储过程,参考笔记<MySQL存储过程和函数创建>以及官网:https://dev.mysql.com/doc/refman/5.7/en/create-pro ...

  5. diango中让装了装饰器的函数的名字不是inner,而是原来的名字

    让装了装饰器的函数的名字不是inner,而是原来的名字 from functools import wraps def wrapper(func): @wraps(func) # 复制了原来函数的名字 ...

  6. s3c2440裸机-代码重定位、清bss的改进和位置无关码

    1.代码重定位的改进 用ldr.str代替ldrb, strb加快代码重定位的速度. 前面重定位时,我们使用的是ldrb命令从的Nor Flash读取1字节数据,再用strb命令将1字节数据写到SDR ...

  7. Centos8 配置静态IP

    安装centos 8之后,重启启动网络时,会出现以下报错 报错信息如下: Failed to start network.service: Unit network.service not found ...

  8. JavaScript-----7.循环

    1.循环 在JS中主要有以下三种类型的循环 for循环 while循环 do...while循环 2. for循环 2.1 语法结构如下: for (初始化变量: 条件表达式: 操作表达式) { // ...

  9. Vue.js学习笔记--菜鸟搭建一个企业级vue的项目

    vue.js新手搭建一个企业项目,从0开始 前置条件: node.npm请先安装配置好 下面开始: npm 下载vue-cli脚手架工具  确认安装成功看到版本号: 初始化项目,选用webpack(p ...

  10. koa2 从入门到进阶之路 (七)

    之前的文章我们介绍了一下 koa koa-static静态资源中间件,本篇文章我们来看一下 koa 中 cookie 和 session 的使用. cookie 是存储于访问者的计算机中的变量.可以让 ...