bodyParser中间件的研究

6 minute read

bodyParser 是使用 express 标配中最基本的一个功能,提供了请求体解析的功能,一般情况下默认的配置就可以使用了,但有时候我们需要做进一步的配置才能满足多样化的需求。

接触 nodejs 已有一段时间了,但最近才开始落实项目,于是使用 express 应用生成器生成了一个应用。开发过程中发现 ajax 提交的数据无法被 express 正确的解析,主要的情况是这样的:

 1// 浏览器端post一个对象
 2$.ajax({
 3  url: '/save',
 4  type: 'post',
 5  data: {
 6    name: 'henry',
 7    age: 30,
 8    hobby: ['sport', 'coding'],
 9  },
10});
11
12// express接收这个对象
13router.post('/save', function (req, res, next) {
14  console.log(req.body); // => { 'info[name]': 'henry','info[age]': '30','hobby[1]': 'sport','hobby[2]': 'coding' }
15});

显然这样的解析结果是不能直接拿来用的,莫名其妙的一个坑,困了我许久。

bodyParser 中间件

bodyParser 中间件用来解析 http 请求体,是 express 默认使用的中间件之一。

使用 express 应用生成器生成一个网站,它默认已经使用了 bodyParser.jsonbodyParser.urlencoded 的解析功能,除了这两个,bodyParser 还支持对 text、raw 的解析。

1app.use(bodyParser.json());
2app.use(bodyParser.urlencoded({ extended: false }));

顾名思义,bodyParser.json 是用来解析 json 数据格式的。bodyParser.urlencoded 则是用来解析我们通常的 form 表单提交的数据,也就是请求头中包含这样的信息: Content-Type: application/x-www-form-urlencoded

常见的四种 Content-Type 类型:

  • application/x-www-form-urlencoded 常见的 form 提交
  • multipart/form-data 文件提交
  • application/json 提交 json 格式的数据
  • text/xml 提交 xml 格式的数据

详细解读 urlencoded

bodyParser.urlencoded 模块用于解析 req.body 的数据,解析成功后覆盖原来的 req.body,如果解析失败则为 {}。该模块有一个属性 extended,官方介绍如下:

The extended option allows to choose between parsing the URL-encoded data with the querystring library (when false) or the qs library (when true). Defaults to true, but using the default has been deprecated.

大致的意思就是:extended 选项允许配置使用 querystring(false)或 qs(true)来解析数据,默认值是 true,但这已经是不被赞成的了。

querystring 就是 nodejs 内建的对象之一,用来字符串化对象或解析字符串。如

1querystring.parse("name=henry&age=30") => { name: 'henry', age: '30' }

那么,既然 querystring 已经能完成对 urlencode 的解析了,为什么还需要 qs?qs 又是什么?

qs 介绍

qs 是一个 querystring 的库,在 qs 的功能基础上,还支持更多的功能并优化了一些安全性。比如,对象解析的支持:

 1// 内建对象 querystring
 2querystring.parse("info[name]=henry&info[age]=30&hobby[1]=sport&hobby[2]=coding") =>
 3  {
 4    'info[name]': 'henry',
 5    'info[age]': '30',
 6    'hobby[1]': 'sport',
 7    'hobby[2]': 'coding'
 8  }
 9
10// 第三方插件 qs
11qs.parse("info[name]=henry&info[age]=30&hobby[1]=sport&hobby[2]=coding") =>
12  {
13    info: {
14      name: 'henry',
15      age: '30'
16    },
17    hobby: [ 'sport', 'coding' ]
18  }

可以看出,querystring 并不能正确的解析复杂对象(多级嵌套),而 qs 却可以做到。

但是 qs 也不是万能的,对于多级嵌套的对象,qs 只会解析 5 层嵌套,超出的部分会表现的跟本文头部的那种情况一样;对于数组,qs 最大只会解析 20 个索引,超出的部分将会以键值对的形式解析。

作为一个中间件,qs 必须要为性能考虑,才会有如此多的限制,express 也默认使用 qs 来解析请求体。

理论上来说,form 表单提交不会有多级嵌套的情况,而 urlencoded 本身也是 form 的内容类型,因此,bodyParser.urlencoded 不支持多级嵌套也是很合理的设计。

那么,如果我们非要上传一个十分复杂的对象,应该怎么办?

解决方案

出现这个问题的根本原因是:我以 form 的形式去提交了一个 json 数据。

jquery 默认的 content-Type 配置的是 application/x-www-form-urlencoded

因此更改 ajax 请求参数:contentType: "application/json",并将数据转成 json 提交,问题就解决了。

 1// 浏览器端post一个对象
 2$.ajax({
 3  url: '/save',
 4  type: 'post',
 5  contentType: 'application/json',
 6  data: JSON.stringify({
 7    name: 'henry',
 8    age: 30,
 9    hobby: ['sport', 'coding'],
10  }),
11});
12
13// express接收这个对象
14router.post('/save', function (req, res, next) {
15  console.log(req.body); // => { name: 'henry', age: 30, hobby: [ 'sport', 'coding' ] }
16});

参考资料

大多时候,我们只知道如何去使用,而不知道为什么这么用。