微信小程序Post方法提交数据

问题描述

微信小程序使用POST方法向Spring Boot 后端提交数据,但是后端无法得到期待的数据;

后端Controller方法使用@RequestBody注解DTO,前端需要传入JSON字符串参数,然后Spring MVC完成JSON字符串和DTO的转换;后端接收到了数据,但是字段全部为null;

后端Controller方法已经通过postman测试;

场景还原

//微信小程序请求代码:
let obj=new Object();
//....设置obj的属性
wx.request({
    url:"url",
    method:"POST",
    data:{
        "dtoName":obj
    }
});
//Spring Boot后端Controller方法
@PostMapping("add")
public Result addDTO(@RequestBody DTO dto){
	return dtoService.save(dto);
}

问题分析

后端代码不存在功能性缺陷,通过调试发现接收到参数,并且进入了Service层,所以问题应该在微信小程序发送的数据不合适,也就是微信小程序发送数据的格式或者发送数据的方式不是后端期待的方式;

问题解决

百度后,发现有一种做法是当请求方法为POST时,指定content-type的值为“application/x-www-form-urlencoded”;但是这时后台会报415错误:

Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]

所以,content-type的值只能为application/json,这是因为我们使用了@RequestBody注解,Spring MVC只有在content-type为application/json的时候才会做转换,否则就报“Unsupported Media Type”的415错误;

去微信小程序官网文档查看wx.request的用法,知道content-type的默认值为"application/json";也就是将对数据进行 JSON 序列化;这说明小程序端发送数据的方式没有问题;因为会“对数据进行 JSON 序列化”,那么数据自然就是data对象;我们的data实际上这样一个对象:它有一个“dtoName”的属性,其值为我们构造的对象;这样就能解释为什么接收到数据(说明媒体类型是合适的),但是字段全部为null,也就是发送数据的格式有问题;修改如下:

wx.request({
    url:"url",
    method:"POST",
    data:{
        fieldOne:"value",
        fieldTwo:"value"
    }
});

这样就解决了使用@RequestBody注解,接收到数据字段为null的问题;

涉及原理

实际上,POST提交数据时可以有多种格式:form-data、x-www.form-urlencodeed、raw、binary等;@RequestBody注解正是指示框架将body里的内容按照JSON字符串解析成相应对象,所以当content-type不是application/json的请求到达时,自然不能处理,报错415;值得一提的是,使用postman测试的时候正是指定post的格式为raw;另外,即便使用“application/x-www-form-urlencoded”的方式,data的格式仍然需要为键值对;

官方文档中的说明如下:

最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String 。转换规则如下:
对于 GET 方法的数据,会将数据转换成 query
string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…) 对于 POST 方法且 header[‘content-type’] 为 application/json 的数据,会对数据进行 JSON
序列化 对于 POST 方法且 header[‘content-type’] 为
application/x-www-form-urlencoded 的数据,会将数据转换成 query string

(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)…)

所以,我们也可以直接将一个JSON字符串传递给data,以实现提交JSON数据的需求(比如提交数组时,并不需要手动构造data的键值对,只需要将数组转换为JSON字符串,传递给data即可);

总而言之,data的构造方式应该同小程序发送方式(json还是x-www-form-urlencoded)以及后端接收方式相一致;