【发布时间】:2019-05-08 18:03:49
【问题描述】:
为什么 JSON 在绑定到 Web 请求时会以括号表示法呈现?
我有一个应用程序可以连续调用 2 次 REST 控制器。
首先,从表单中读取地址,对其进行序列化、AJAX 处理到验证端点,然后作为响应对象有效负载返回给 UI/JS。这部分工作正常。
$.ajax({
method: 'POST',
url: '/validate-shipping-address',
data: $shippingInfoForm.serialize()
}
@PostMapping(path = "/validate-shipping-address")
public RestResponse<Address> validateShippingAddress(
@ModelAttribute("shippingForm") ShippingForm shippingForm) {
return validateAddress(shippingForm.getAddress());
}
如果响应成功,则该有效负载(地址)将直接发送到第二个端点的 AJAX 调用中。此请求以400 结束,并且从未进入实际方法。
$.ajax({
method: 'POST',
url: '/shipping-stuff',
data: {
"shippingAddress": validationResponse.payload,
"shipDate": shipDate,
"csrfToken": csrfToken
}
}
@PostMapping(path = "/shipping-stuff")
public RestResponse<?> doShippingStuff(
@RequestParam(name = "shippingAddress") Address shippingAddress,
@RequestParam(name = "shipDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date shippingDate) {
doStuff(); // Never hit
}
经过大量分析,问题在于 Spring MVC 无法反序列化作为 Address 对象传递的地址。在请求中,我看到地址字段呈现为address[city]、address[state] 等,而不是标准的点符号address.city、address.state(因为第一个请求有它)。如果我使用括号符号作为参数名称通过请求手动访问它们,它将提取值。例如request.getParameter("address[city]");.
当我使用 Chrome 开发工具调试器检查第一个响应和进入第二个 AJAX 调用的对象时,它们看起来像有效的 JSON。不过,Chrome 中的 Network:Form Data 部分有所不同 - 它显示第一个成功调用的点符号和第二个不成功调用的括号符号。
表单数据(第一次调用):
address.firstName=John&address.lastName=Doe&address.addressLine1=123+Main+St&address.city=New+York+City&address.state=NY&address.postalCode=12345&csrfToken=XXXX
表单数据(第二次调用):(%5B = '[' and %5D = ']')
shippingAddress%5BfirstName%5D=John&shippingAddress%5BlastName%5D=Doe&shippingAddress%5BaddressLine1%5D=123+MAIN+ST&shippingAddress%5Bcity%5D=New+York+City&shippingAddress%5Bstate%5D=NY&shippingAddress%5BpostalCode%5D=12345&shippingAddress%5BzipFour%5D=6789&shipDate=2019-05-25&csrfToken=XXXX
所以真的有两个子问题:
(1) JS 处理中是否有什么东西会导致地址以括号形式而不是普通的 JSON 形式传递?我想如果我能解决这个问题,那么 Spring MVC 应该可以正常工作。
(2) 有没有办法让 Spring MVC 能够在不诉诸 JSON.stringify 然后直接在控制器代码中使用 Gson/ObjectMapper 解析的情况下处理这个问题?
我已经尝试了所有我能想到的涉及自定义包装对象的排列,JSON.stringify、@RequestParam、@ModelAttribute 和裸(无注释)。我还尝试对整个 AJAX 有效负载进行字符串化,并在控制器中的包装器对象上使用 @RequestBody,但随后调用失败,因为未检测到 csrfToken。
我已经阅读了this、this 和this,这些都为上述尝试提供了信息。
目前,我已经解决了 JSON.stringify 和 Gson(上面的选项 2)的问题,但宁愿让 Spring MVC 自动完成工作。
解决方法:
$.ajax({
method: 'POST',
url: '/commercial-checkout/shipping-stuff',
data: {
"shippingAddress": JSON.stringify(shippingAddress),
"shipDate": shipDate,
"csrfToken": csrfToken
}
});
@PostMapping(path = "/shipping-stuff")
public RestResponse<?> doShippingStuff( //
@RequestParam(name = "shippingAddress") String shippingAddressJson,
@RequestParam(name = "shipDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date shipDate) {
Address address = gson.fromJson(shippingAddressJson, AddressImpl.class);
}
【问题讨论】:
标签: javascript java jquery ajax spring-mvc