1.1问题起源:

非get请求,前端字段为非string类型,结果传到后端node层,字段变成string类型,大部分情况是这样,起 了个怪,有的时候发现没有变成string。导致用egg-validata很是困惑,该设置成什么类型,很是蛋疼,样例如下:

前端非get请求字段:

image.png

后端egg解析出来:

http请求和Node端解析请求body问题小结 - 图2


1.2原因:

请求头中默认:
Content-Type: application/x-www-form-urlencoded

请求body如下(注意Form Data):

http请求和Node端解析请求body问题小结 - 图3

Content-Type 默认为 application/x-www-form-urlencoded(代表数据是表单数据类型),所以,提交的数据按照key1=val1&key2=val2 的方式进行序列化编码,key 和 val 都进行了 encode 转码,到了node端,框架底层会根据mime(即content-type)类型,调用不同的方法解析,如果为application/json,直接JSON.parse(req.rawBody),,如果为application/xml,则调用xml2js第三方模块解析为xml,,如果为application/x-www-form-urlencoded,则使用类似解析查询字符串方法解析。

参考: https://imququ.com/post/four-ways-to-post-data-in-http.html(博客)
https://eggjs.org/zh-cn/basics/controller.html#body(egg文档)
nodejs深入浅出第8章构建web应用第8.2数据上传


1.3解决:

设置请求头header中content-type: ‘application/json’,
but报错:400 ,,invalid JSON, only supports object and array,
koa内置bodyparse解析,为啥出现这个问题,还没找到原因,报错模块为co-body(https://github.com/cojs/co-body
后来查到的原因:
body如下,虽然设置content-type,但是最后请求body还是类似content-type:application/x-www-form-urlencoded这种类型,所以node端根据content-type来解析请求body,发现不是json,然后报错invalid JSON

http请求和Node端解析请求body问题小结 - 图4

暂时需要手动JSON.stringfy(data),,,按道理,请求工具库request或者jquey.ajax()底层应该会转换吧,不知道为啥没?

body如下:

http请求和Node端解析请求body问题小结 - 图5

1.4结果:

在node层可以拿到原来的数据,不在有格式转换


2.1 根据以上方法,有出现新的问题

在使用Fetch.POST or Fetch.PUT 方法,会报错,Cannot create property ‘_csrf’……..

2.2

  1. Fetch.POST = function(params, opt) {
  2. params = buildOptions(params);
  3. return Fetch({
  4. method: 'POST',
  5. type: 'json',
  6. contentType: 'application/json',
  7. processData: false,
  8. ...params
  9. }, buildHook(opt));
  10. };
  11. function buildOptions(params) {
  12. const p = { ...params };
  13. attachCSRF(p);
  14. p.data = JSON.stringify(params.data);
  15. return p;
  16. }

直接调用Fecth的方法,如post,会JSON.strngfy(),导致下面函数调用报错

  1. /**
  2. * 附加csrf参数
  3. * @param params
  4. */
  5. function attachCSRF(params) {
  6. params.data._csrf = window._youku_token_;
  7. }

2.3 总结

Fetch({
method: post,
……
data: JSON.stringfy(data) //需要手动序列化
})

Fetch.POST({})等,则不必序列化,fetch函数会做,否则报错

3 补充:处理请求数据

对于Jquery or zepote 等封装的请求库,都有processData可配置项,默认为true,,同时content-type页默认为表单格式。
image.png
但是我们现在普遍使用json格式提交数据。不管那种格式,最终处理完的数据(传给xhr.send()),都必须是字符串,可以是表单格式(a=b&c=d),也可以是json.Strignfy转成字符串的json格式,也可以是xml格式(现在基本不用),所以这些封装过的ajax方法,都会在内部把data选项&&值是对象类型转换成string,但是默认是转成表单格式的。
列如:
image.png
image.png
所以为了避免被自动转换成表单格式,就应该在data选项就把对象用json.stringfy()处理成json类型(也就是字符串),内部判断是string类型,就不会再处理。保险起见,也可以同时设置processData为false。
image.png

相反的 axiose默认值content-type 默认值为application/json
image.png

参考
1 犀牛书 pg561 (专讲jquery.ajax) pg 494 (编码请求主体)
2 红皮书 pg 577 (ajax)