AJAX / Spring MVC: рендеринг объекта JSON в скобочных обозначениях в запросе POST - PullRequest
1 голос
/ 08 мая 2019

Почему при привязке к веб-запросу JSON будет отображаться в скобочных обозначениях?

У меня есть приложение, которое выполняет 2 вызова REST Controller подряд.

Сначала адрес читается из формы, сериализуется, AJAXed в конечную точку проверки и возвращается в UI / JS в качестве полезной нагрузки объекта Response.Эта часть работает нормально.

$.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 dev для проверки ответа первого и объекта, поступающего во второй вызов AJAX, они выглядят как действительный JSON.Раздел «Сеть: данные формы» в Chrome отличается - он показывает точечную запись для первого успешного вызова и скобку для второго неудачного вызова.

Данные формы (первый вызов):

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 = '[' и% 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

Таким образом, на самом деле есть 2 подвопроса:

(1) Есть ли в обработке JS что-то, что могло бы привести к тому, что адрес будет передан в форме скобок вместо обычной формы JSON?Я думаю, если бы я мог обойти это, тогда Spring MVC должен работать как обычно.

(2) Есть ли способ заставить Spring MVC справиться с этим, не прибегая к JSON.stringify, а затем анализируя с помощью Gson/ ObjectMapper прямо в коде контроллера?

Я перепробовал все перестановки, которые я могу придумать, включая пользовательские объекты-оболочки, JSON.stringify, @RequestParam, @ModelAttribute и голые (без аннотаций).Я также попытался структурировать всю полезную нагрузку AJAX и использовать @RequestBody для объекта-обертки в контроллере, но затем вызов не удался, так как csrfToken не обнаружен.

У меня естьПрочитайте это , это и это , которые сообщали о попытках выше.


Пока я обходилпроблема с 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);
}

1 Ответ

1 голос
/ 09 мая 2019

Согласно вашему комментарию,

Я также пытался структурировать всю полезную нагрузку AJAX и использовать @RequestBody для объекта-оболочки в контроллере, но затем происходит сбой вызова, так как csrfToken не обнаружен.

Когда вы используете @RequestBody, вам нужно создать соответствующий объект POJO для десериализации вашего JSON.Также вам нужно добавить свойство типа контента в ваш AJAX, чтобы указать сервер, который вы отправляете 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..
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...