【问题标题】:AJAX/Spring MVC: JSON Object rendering in bracket notation in POST requestAJAX/Spring MVC:在 POST 请求中以括号表示法呈现 JSON 对象
【发布时间】: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.cityaddress.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

我已经阅读了thisthisthis,这些都为上述尝试提供了信息。


目前,我已经解决了 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


    【解决方案1】:

    根据您的评论,

    我还尝试对整个 AJAX 有效负载进行字符串化并使用 @RequestBody 在控制器中的包装对象上,然后调用 由于未检测到 csrfToken 而失败。

    当您使用@RequestBody 时,您需要创建相应的 POJO 对象来反序列化您的 JSON。此外,您还需要在 AJAX 中添加 content-type 属性以指示您正在发送 JSON 的服务器。

    $.ajax({
        method: 'POST',
        data: 'json',
        content-type: 'application/json',
        url: '/commercial-checkout/shipping-stuff',
        data: {
                "shippingAddress": JSON.stringify(shippingAddress),
                "shipDate": shipDate,
                "csrfToken": csrfToken
        }
    });
    

    添加一个我提到的 POJO,

    public class AddressPOJO {
    
       shippingAddress,
       shipDate,
       csrfToken  
    
      //getter / setters
    }
    

    修改你的控制器方法,

    @PostMapping(path = "/shipping-stuff", consumes = "application/json")
    public RestResponse<?> doShippingStuff( @RequestBody  AddressPOJO addressPJO) {
    
        // do your logic..
    }
    

    【讨论】:

    • 感谢您的回复。我确实尝试了这个版本,尽管我没有在包装器对象中包含 csrfToken。我也没有设置内容类型。我会用它再试一次,看看它是否有效。
    猜你喜欢
    • 2016-11-28
    • 2015-09-22
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2014-03-28
    • 2019-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多