使用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
参数。