Используя 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?




Параметры @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.
Очень хорошо артикулированный
Да - создать объект-оболочку 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 как тип (для поддержки загрузки файла). Загрузка работает, если я ТОЛЬКО обрабатываю файл. Но у меня также есть объект книги, который передается в представление, а поля берутся из моей формы. Значит, я смогу создать оболочку для книги и составного файла?
Создайте другой класс и создайте объекты, получатель, установщик для кредитной карты, адрес доставки, затем используйте объект @requestBody нового класса