ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ## 介绍 在处理函数前转换得到的请求体,使得 req.body属性可用的中间件。 > 由于req.body的基于用户控制的输入,此对象中的所有属性和值都是不可信的,应在信任之前进行验证。 例如,req.body.foo.toString()可能会以多种方式失败,例如foo属性可能不存在,也可能不是字符串,并且toString可能不是函数,而是字符串或其他用户输入。 > > 常见的四种Content-Type类型: > * application/x-www-form-urlencoded 常见的form提交 > * multipart/form-data 文件提交 > * application/json 提交json格式的数据 > * text/xml 提交xml格式的数据 这个中间件不会解析multipart body,由于这种消息体很复杂而且也很大。对于multipart bodies,可以使用以下中间件: * busboy and connect-busboy * multiparty and connect-multiparty * formidable * multer 本模块提供了以下解析器 * JSON体解析器 * Raw解析器 * Text解析器 * URL-encoded form 解析器 可以通过body-parser 对象创建中间件,当接收到客户端请求时所有的中间件都会给req.body 添加属性,请求体为空,则解析为空{} (或者出现错误)。 ## bodyParser.json([options]) 返回只解析json的中间件,只查看Content-Type类型匹配type选项。允许请求体任意Unicode编码,支持 gzip 和 deflate 编码。 **选项** inflate 默认为true。当设置为true,被deflate(压缩)的请求体会被inflated,为false时,不会deflated 请求体。 limit 控制请求体最大大小,默认为100kb,当为数字时会转换为bytes,当为字符串时,value值会通过 bytes库 转换为字节大小。 reviver 此选项会通过JSON.parse直接传给其第二个参数。 strict 默认为true,当为true时只接受数组和对象,当为false时会接受任何JSON.parse 能接受的。 type type 选项用来决定中间件要解析媒体类型,默认为application/json。选项可以是一个字符串、字符串数组或函数。当为字符串时,可以直接通过type-is 库直接传递给选项,字符串也可以为一个扩展名(例如json)、mime 类型(application/json、/ 、*/json)。 verify verify选项,若缺失则为一个函数function(req,res,buf,encoding),buf为一个Buffer。 ## bodyParser.raw([options]) 将请求体内容作为 Buffer 来处理,并返回。支持gzip deflate 压缩。 inflate 默认为true。当设置为true,被deflate(压缩)的请求体会被inflated,为false时,不会deflated 请求体。 limit 控制请求体最大大小,默认为100kb,当为数字时会转换为bytes,当为字符串时,value值会通过 bytes库 转换为字节大小。 reviver 此选项会通过JSON.parse直接传给其第二个参数。 strict 默认为true,当为true时只接受数组和对象,当为false时会接受任何JSON.parse 能接受的。 type type 选项用来决定中间件要解析媒体类型,默认为application/octet-stream。选项可以是一个字符串、字符串数组或函数。 若不是函数,type 选项传递到type-is 库,它可以说扩展名(如 bin),mime 类型(如 application/octet-stream)或者带通配符的mine type(如*/* or application/*)。 如果函数,type选项会以fn(req)执行并且如果返回真值请求会被转换。 verify verify选项,若缺失则为一个函数function(req,res,buf,encoding),buf为一个Buffer。 ## bodyParser.raw([options]) 将请求提内容作为字符串来处理,并返回。支持gzip deflate 压缩。 ## bodyParser.urlencoded([options]) 中间件只解析urlencoded 请求体,并返回,只支持UTF-8编号,支持gzip deflate 压缩。 **选项** extended 若为true,使用qs库转换URL-encoded data,为false则使用querystring库。extended符号允许富对象和数组被编码为URL-encoded的类型,也可以是一个JSON编码的数据。 默认值为true,但使用默认值已经不赞成。请研究 qs 和 querystring的区别并选择合适的设置。 inflate 控制请求体最大大小,默认为100kb,当为数字时会转换为bytes,当为字符串时,value值会通过 bytes库 转换为字节大小。 limit 控制请求体最大大小,默认为100kb,当为数字时会转换为bytes,当为字符串时,value值会通过 bytes库 转换为字节大小。 parameterLimit 指定URL-encoded数据中parameters最长长度,若请求包含的parameter大于此数,返回413到客户端。默认1000。 type type 选项用来决定中间件要解析媒体类型,默认为application/x-www-form-urlencoded。选项可以是一个字符串、字符串数组或函数。 若不是函数,type 选项传递到type-is 库,它可以说扩展名(如 urlencoded),mime 类型(如 application/x-www-form-urlencoded)或者带通配符的mine type(如application/x-www-form-urlencoded)。 如果函数,type选项会以fn(req)执行并且如果返回真值请求会被转换。 verify verify选项,若缺失则为一个函数function(req,res,buf,encoding),buf为一个Buffer。 ## querystring与qs ### querystring querystring就是nodejs内建的对象之一,用来字符串化对象或解析字符串。如 ~~~ querystring.parse("name=henry&age=30") => { name: 'henry', age: '30' } ~~~ ### qs qs是一个querystring的库,在qs的功能基础上,还支持更多的功能并优化了一些安全性。比如,对象解析的支持: ~~~ // 内建对象 querystring querystring.parse("info[name]=henry&info[age]=30&hobby[1]=sport&hobby[2]=coding") => { 'info[name]': 'henry', 'info[age]': '30', 'hobby[1]': 'sport', 'hobby[2]': 'coding' } // 第三方插件 qs qs.parse("info[name]=henry&info[age]=30&hobby[1]=sport&hobby[2]=coding") => { info: { name: 'henry', age: '30' }, hobby: [ 'sport', 'coding' ] } ~~~ querystring并不能正确的解析复杂对象(多级嵌套),而qs却可以做到。 但是qs也不是万能的,对于多级嵌套的对象,qs只会解析5层嵌套,超出的部分会表现的跟本文头部的那种情况一样;对于数组,qs最大只会解析20个索引,超出的部分将会以键值对的形式解析。 理论上来说,form表单提交不会有多级嵌套的情况,而urlencoded本身也是form的内容类型,因此,bodyParser.urlencoded不支持多级嵌套也是很合理的设计。 那么,如果我们非要上传一个十分复杂的对象,应该怎么办? ### 解决方案 ## 用例 在express项目中,通常顺序调用body-parser所提供的parser,这样当一个parser无法满足post参数解析条件时,还可以使用另一个parser进行解析(在某些特殊的请求中,有可能所有parser均无法解析)。 ~~~ app.use(bodyParser.raw()); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false }); ~~~ 但body-parser的各个parser在解析的过程中,若对满足解析条件的post参数进行了解析,req作为一个stream对象,已经被消耗,无法再使用另一个parser对post参数解析,也即post参数只能被第一个满足解析条件的parser进行解析。因此即使先后调用raw、json、urlencoded这三个parser,也只能得到一个body,具体格式由各parser的调用次序及post参数满足的解析条件决定。JSON化的body和raw body如同鱼与熊掌,二者不可得兼。 但在有些场合下,可能需要在解析body的同时使用raw body进行其他操作,如某些应用场景下就需要使用raw body参与签名运算以对访问者进行鉴权。 其实body-parser提供了一个办法在解析post参数的同时获取raw body,那就是在调用parser的时候传入的参数中带verify回调函数: ~~~ app.use(bodyParser.json({ verify: function (req, res, buf, encoding) { req.rawBody = buf; } })); app.use(bodyParser.urlencoded({ extended: false, verify: function (req, res, buf, encoding) { req.rawBody = buf; } })); ~~~ verify参数本身是用于对请求的校验,当校验失败的时候通过抛出error来中止body-parser的解析动作,在这里被借用来实现post参数raw body的获取。 这样一来,在将post参数解析成JSON化的req.body的同时,body-parser还会将raw body赋值给req.rawBody(当然,内存也占用了两份,这是需要注意的地方,如果应用中有大数据量的POST请求,那可就要注意了)。 ## 参考资料 [bodyParser中间件的研究](https://segmentfault.com/a/1190000004407008) [如何在nodejs express项目中使用body-parser的同时获取JSON化的body和raw body](https://segmentfault.com/a/1190000003061925)