Spring Boot: могу ли я публиковать несколько объектов как несколько @RequestParams в службе REST?

Используя Spring Boot, я создал игрушечный REST-сервис с одним ресурсом, например:

@RestController
@RequestMapping("/checkout")
public class CheckoutRestController {

    @PostMapping("/book")
    public boolean buyBook(@RequestParam CreditCard creditCard, @RequestParam ShippingAddress shippingAddress) {
        return true;
    }
}

И CreditCard, и ShippingAddress - это POJO (простые старые объекты Java), которые я закодировал).

Я пробовал отправлять сообщения на эту конечную точку с помощью этой полезной нагрузки:

{
    "creditCard" : {
        "nameOnCard":"foo",
        "number":12345678,
        "expiryMonth":12,
        "expiryYear":2018,
        "ccv":100
    },
    "shippingAddress" : {
        "steert":"foor",
        "houseNumber":"1a",
        "city":"bar",
        "state":"bazz",
        "country":"buzz",
        "zipCode":"booz"
    },
}

Но получаю ошибку:

{
    "timestamp": "2018-03-13T11:36:52.745+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "Required CreditCard parameter 'creditCard' is not present",
    "path": "/checkout/book"
}

Я знаю, что обходным путем было бы обернуть оба POJO в оболочку запроса, но я бы не стал, если мне действительно не нужно.

Можно ли разместить два аннотированных объекта @RequestParam? Если да, то как будет выглядеть JSON?

Создайте другой класс и создайте объекты, получатель, установщик для кредитной карты, адрес доставки, затем используйте объект @requestBody нового класса

Sumesh TG 13.03.2018 13:16
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
1
10 576
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Параметры @RequestParam - это параметры запрос, а не параметры тела.

Это означает, что ваш метод:

@PostMapping("/book")
public boolean buyBook(@RequestParam CreditCard creditCard, @RequestParam 
    ShippingAddress shippingAddress) {

        return true;
}

Ожидает следующий запрос:

POST /checkout/book?creditCard=<...>&shippingAddress=<...>

Однако Spring не знает, как преобразовать параметр запроса String в CreditCard или в ShippingAddress.

Вы можете решить эту проблему, реализовав Converter следующим образом:

public class StringToCreditCardConverter implements Converter<String, CreditCard> {

    @Override
    public CreditCard convert(String source) {
         <...>
    }
}

Однако я не рекомендую это делать, поскольку это не стандарт для органов запросов и может вызвать путаницу и проблемы с ремонтопригодностью.

Вместо этого рекомендуется следующий способ:

@PostMapping("/book")
public boolean buyBook(@RequestBody BookCreationRequest bookCreationRequest) {
        CreditCard creditCard = bookCreationRequest.getCreditCard();
        ShippingAddress shippingAddress = bookCreationRequest.getShippingAddress();
        ...
}

С bookCreationRequest, содержащим поля CreditCard и ShippingAddress (примечание: вы можете использовать, например, lombok, чтобы уменьшить шаблонный код):

public class BookCreationRequest {
    private ShippingAddress shippingAddress;
    private CreditCredit creditCard;

    public ShippingAddress  getShippingAddress() {...}
    public CreditCard       getCreditCard() {...}

    public BookCreationRequest(ShippingAddress shippingAddress, CreditCard creditCard) {
        this.creditCard = creditCard;
        this.shippingAddress = shippingAddress;
}

Что тогда ожидало бы запроса JSON следующим образом:

POST /checkout/book
Payload:
{
    "creditCard": {
        ... 
    },
    "shippingAddress": {
        ...
    }
}

Обратите внимание, что в запросе может быть только один параметр @RequestBody.

Очень хорошо артикулированный

Sampath 27.12.2020 18:48

Да - создать объект-оболочку Request. Это не обходной путь - это рекомендуемый подход.

Вы видите в своем json-файле:

{ //wrapper
 {}, //first complex data object / mapped to pojo
 {} //second complex data object / mapped to pojo
}

1) Вы можете легко применить проверку к объекту запроса с помощью @Valid и, следовательно, проверить оба POJO.

2) Вам не нужно беспокоиться о порядке в полезной нагрузке.

3) Вы неправильно используете @RequestParam. @RequestBody - это то, что отображает всю полезную нагрузку POST. Вам необходимо удалить аннотацию @RequestParam и использовать вместо нее @RequestBody.

Хорошо, тогда это должно решить мою проблему. Я использую enc-multipart как тип (для поддержки загрузки файла). Загрузка работает, если я ТОЛЬКО обрабатываю файл. Но у меня также есть объект книги, который передается в представление, а поля берутся из моей формы. Значит, я смогу создать оболочку для книги и составного файла?

chrips 03.03.2019 16:08

Другие вопросы по теме