【问题标题】:Spring Boot: Can I post multiple objects as multiple @RequestParams to a REST service?Spring Boot:我可以将多个对象作为多个 @RequestParams 发布到 REST 服务吗?
【发布时间】:2018-08-21 15:40:50
【问题描述】:

使用 Spring Boot,我使用单个资源构建了一个玩具 REST 服务,如下所示:

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

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

CreditCardShippingAddress 都是我编写的 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 会是什么样子?

【问题讨论】:

  • 创建另一个类并创建对象,gettter,setter for CreditCard,ShippingAddress 然后使用新类的@requestBody 对象

标签: java spring rest spring-boot


【解决方案1】:

@RequestParam 参数是查询 参数,而不是正文参数。

这意味着你的方法:

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

        return true;
}

预期以下请求:

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

但是,Spring 不知道如何将 String 查询参数转换为 CreditCardShippingAddress

您可以通过实现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 包含 CreditCardShippingAddress 字段(注意:您可以使用例如 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 参数。

【讨论】:

  • 非常清楚
【解决方案2】:

是的 - 创建一个包装请求对象。 这不是一种解决方法 - 它实际上是推荐的方法。

您在 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 作为类型(支持上传文件)。如果我只是处理文件,则上传工作。但是我也有一个传递给视图的书籍对象,并且字段是从我的表单中获取的。所以我应该能够为 Book 和 Multipart File 创建一个包装器?
猜你喜欢
  • 2023-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-20
  • 2019-02-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多