前后台数据传输的一次爬坑
不多说,直接上主要错误提示:Status Code:400 Mauvaise Requête
顺便把异常的Response Headers:
下面上上前后端的代码,以供比较和说明:
后端的js:(只上封装对象和ajax部分的)
//封装传入参数 var record = { id : $("#rowId")[0].dataset.rowId, employeeNo : window.localStorage.EMPLOYEENO, name : window.localStorage.EMPLOYEENAME, projectNo : projectNo, projectName : projectName, type : type,//0:系统项目,1:自建项目 //beginDate : strData2Int($("#beginDate").val()),//对应实体类里面是Date类型的 //endDate : strData2Int($("#endDate").val()), asdf : "sdjshjk", } $.ajax({ type: "POST", url: "admin/plantask/edit/save?type="+operate,//向后台传一个操作类型的字符串 dataType: "json", //contentType: 'application/json', data: record,//向后台传的一个对象,里面是对应操作的一条数据,因为这个页面被新增和修改复用,所有会额外向后台传一个操作类型的字符串 success: function (flag) {}
})
控制器代码:
@ResponseBody @RequestMapping(value="admin/plantask/edit/save", method = RequestMethod.POST) public int save(PlanTask obj, @RequestParam("type")String type) { int flag; return 0; }
先直接说错误点吧,想必大家也没耐心看完爬坑的过程,直接揭晓答案吧,
错就错在对象record里面的属性(type),和地址栏里面定义的操作类型健值(刚好也是type)重名了,重名了,重名了,,,好了,其实最开始如果知道是这个的话,一改就OK了!!!
下面说下慢慢揭开迷雾的过程,及明白的一些东西:
(1)最开始,因为错误码报400,没找到接口,所以给后台type参数,加了上了@RequestParam("type");-----------(跟@pathVariable的区别(可能敲错了))
(2)因为Response Headers:里面有一句Content-Language:fr;以为是页面默认编码的问题,取模板页改了<meta http-equiv="content-language" content="zh-CN" />,差点把<html lang="en">这个也给改了
(3)后面竟然怀疑POST能不能地址栏传参(说实话,已经怀疑人生了!!),答案是肯定的,post不但支持,还安全,参数允许长度还比get大好多。。。
(4)。。。。。
当然,以上种种想法并没有解决问题,于是用了分解问题的办法,也就是想具体定位问题,也是最后水落石出的关键,拆分过程:
(1)当然是先验证接口(不过这里说一下,最开始是绕过了后台接收”对象 + 字符串参数”的方法,用了直接前后端分别转包拆包,中间用json字符串传递的方法,虽然万能,但实在麻烦和不易于阅读理解,后面会附上具体代码,也算是一种实现方式),让后台接口只接收一个对象、或一个字符串
(a)只接收单个对象,最开始,我没注释掉两个日期类型属性前,也是保错的,也是吧参数分开传才发现(为什么后台拆json字符串两个日期的值一样),于是发现,其实js封装对象的方法有N种,比如:
var obj = {} obj["id"] = $("#rowId")[0].dataset.rowId; obj["employeeNo"]=window.localStorage.EMPLOYEENO; obj["name"] = window.localStorage.EMPLOYEENAME; obj["projectNo"] = projectNo; obj["projectName"] = projectName; //中括号,里面双引号,定义健,= 等号赋值的方法; var obj = {} obj.id = $("#rowId")[0].dataset.rowId; obj.employeeNo = window.localStorage.EMPLOYEENO; obj.name = window.localStorage.EMPLOYEENAME; obj.projectNo = projectNo; obj.projectName = projectName; //直接对象‘点’属性,= 等号赋值的方法 var obj = { "id" : $("#rowId")[0].dataset.rowId, "employeeNo" : window.localStorage.EMPLOYEENO, "name" : window.localStorage.EMPLOYEENAME, "projectNo" : projectNo, "projectName" : projectName }
//全部在一对{}大括号里面,直接“健”:值的方法(这里健用了双引号,其实不用也是可以的,如最开始的js部分)
最后说下,这里定义obj之后,定义健及给健赋值的方式(点、中括号、冒号),还有每个健直接的分隔符,就是最后的,是逗号还是分号
也就是,目前一共有了四种在js定义对象的方法,两优两劣,各有所爱
回到正题,分开后当然是OK!的,于是开始单参数传递字符串
(b)去掉对象,单参数地址栏传递字符串
发现debug看到type显示的值为:type="update,0"(id=20580) hash=0 value="20582"(点开看是:[u, p, d, a, t, e, ,, 0])
也就是说type里面有两个值,一个updat,一个0;讲真,其实到这里其实问题已经解决了,可以恕我**,我当时没理解为什么==、
(5)回到主干,当时和同事讨论后,同事建议我建一个View,把对象和单个字符串拼在一个View里面(当然,这是万能的,但是缺点不用多言。。)
于是,突然发现,我在record对象里面,随意加的键值对,如果后台PlanTask实体类里面如果没有对应属性值接收的话,正常情况后台是获取不到值的,但是如果在接口入参(即形参)加上@RequestParam("js里面定义的键") String str;是可以直接把record里面,或者地址栏里面定义的值取到的(当时震撼了,豁然开朗,一万种传参情况涌了出来,但是入参多了,写的麻烦,不易阅读)
于是另一种完美解决的方案又出来了,必封装拆分json字符串的形式好了很多很多,但终究是绕过了问题,于是。。。
(6)也是为了进一步测试(5)中在接口定义@RequestParam("jsKey") String str;这种方式,把url: "admin/plantask/edit/save?type=value",里面的键type改成了别的,嗖的一下,嗖的一下,嗖的一下,OK!了,至此,一切水落石出了,
就是重名引起的问题,于是回头重新验证了一遍,都是OK的;结束!!!
最后附上,最原始的解决方案,就是封装拆分json字符串的形式的
JS封装:
//封装传入参数 var record = { id : $("#rowId")[0].dataset.rowId, employeeNo : window.localStorage.EMPLOYEENO, name : window.localStorage.EMPLOYEENAME, projectNo : projectNo, projectName : projectName, type : type,//0:系统项目,1:自建项目 //beginDate : strData2Int($("#beginDate").val()), //endDate : strData2Int($("#endDate").val()), initPrecent : $("#initPrecent").val(), achievePrecent : $("#achievePrecent").val(), planTotalHour : $("#planTotalHour").val(), description : $("#description").val().trim(),//去空格 asdf : "sdjshjk", } var param = { "record" : record, "type" : operate//新增还是修改 } $.ajax({ type: "POST", url: "admin/plantask/edit/save?typee="+operate, data: { //"param" : URLEncoder.encoder(JSON.stringify(param), "UTF-8"),//然而这种并不能在js端解决乱码问题 "param" : JSON.stringify(param), }, dataType: "json", data: record, success: function (flag) {} })
后台解析代码:
@ResponseBody @RequestMapping(value="admin/plantask/edit/save", method = RequestMethod.POST) public int save(String param) { String str = HttpHelper.UTF8(param);//首先要解决乱码问题 JSONObject jsonObject = JSONObject.fromObject(str); Map map = (Map)jsonObject; String type = (String) map.get("type");//获取字符串参数 Object plantask = map.get("record");//获取对象 JSONObject json = JSONObject.fromObject(plantask); PlanTask obj = (PlanTask) JSONObject.toBean(json,PlanTask.class); int flag; //do someingreturn 0;
这种方式虽然繁琐一点,但是1、可以不用管重名问题,调用接口也不会报错,因为参数都封装在一个map形式的json里,2、不用考虑时间,但也是两个时间值相同的问题所在
但是,问题解决后,当然会采取最开始想的那种方案,毕竟简单明了,而且没有乱码问题(框架的强大之处)
url: "admin/plantask/edit/save?type=operate",//
问号后面的type=值,中的type(键)随便改成了别的,嗖的一下,嗖的一下,嗖的一下,OK!了,到此,问题水落石出
就是重名引起的,于是回头都验证了一遍,都是OK的