1.1问题起源:
非get请求,前端字段为非string类型,结果传到后端node层,字段变成string类型,大部分情况是这样,起 了个怪,有的时候发现没有变成string。导致用egg-validata很是困惑,该设置成什么类型,很是蛋疼,样例如下:
前端非get请求字段:
后端egg解析出来:
1.2原因:
请求头中默认:
Content-Type: application/x-www-form-urlencoded
请求body如下(注意Form Data):
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
暂时需要手动JSON.stringfy(data),,,按道理,请求工具库request或者jquey.ajax()底层应该会转换吧,不知道为啥没?
body如下:
1.4结果:
在node层可以拿到原来的数据,不在有格式转换
2.1 根据以上方法,有出现新的问题
在使用Fetch.POST or Fetch.PUT 方法,会报错,Cannot create property ‘_csrf’……..
2.2
Fetch.POST = function(params, opt) {
params = buildOptions(params);
return Fetch({
method: 'POST',
type: 'json',
contentType: 'application/json',
processData: false,
...params
}, buildHook(opt));
};
function buildOptions(params) {
const p = { ...params };
attachCSRF(p);
p.data = JSON.stringify(params.data);
return p;
}
直接调用Fecth的方法,如post,会JSON.strngfy(),导致下面函数调用报错
/**
* 附加csrf参数
* @param params
*/
function attachCSRF(params) {
params.data._csrf = window._youku_token_;
}
2.3 总结
Fetch({
method: post,
……
data: JSON.stringfy(data) //需要手动序列化
})
Fetch.POST({})等,则不必序列化,fetch函数会做,否则报错
3 补充:处理请求数据
对于Jquery or zepote 等封装的请求库,都有processData可配置项,默认为true,,同时content-type页默认为表单格式。
但是我们现在普遍使用json格式提交数据。不管那种格式,最终处理完的数据(传给xhr.send()),都必须是字符串,可以是表单格式(a=b&c=d),也可以是json.Strignfy转成字符串的json格式,也可以是xml格式(现在基本不用),所以这些封装过的ajax方法,都会在内部把data选项&&值是对象类型转换成string,但是默认是转成表单格式的。
列如:
所以为了避免被自动转换成表单格式,就应该在data选项就把对象用json.stringfy()处理成json类型(也就是字符串),内部判断是string类型,就不会再处理。保险起见,也可以同时设置processData为false。
相反的 axiose默认值content-type 默认值为application/json
参考
1 犀牛书 pg561 (专讲jquery.ajax) pg 494 (编码请求主体)
2 红皮书 pg 577 (ajax)