您将如何使用 Spring Boot 处理仅具有可选查询参数的 REST API?

Posted

技术标签:

【中文标题】您将如何使用 Spring Boot 处理仅具有可选查询参数的 REST API?【英文标题】:How would you handle a REST API with only optional query params with Spring Boot? 【发布时间】:2020-06-04 23:58:40 【问题描述】:

我想构建一个返回 Order 对象的简单端点,我可以在其中通过单个查询参数或多个查询参数的组合搜索此订单。所有这些查询参数都是可选的,原因是不同的人会根据不同的 Id 访问这些订单。

例如: /order/items?itemId=itemId&orderId=orderId&deliveryId=deliveryId&packetId=packetId

@GetMapping(path = "/order/items", produces = "application/json")
public Order getOrders(@RequestParam Optional<String> itemId,
                              @RequestParam Optional<String> orderId,
                              @RequestParam Optional<String> deliveryId,
                              @RequestParam Optional<String> packetId)  

当然,我也可以跳过 Java Optional 并使用 @RequestParam(required = false),但这里的问题是如何避免检查查询参数是否为空的 if-else.isPresent() 噩梦?或者是否有一种优雅的方式,取决于参数的星座,进一步传递给我的服务和 Spring Data JPA 存储库。

【问题讨论】:

我总是像 WHERE (:myParam IS NULL OR TABLE.COLUMN = :myParam ) 这样为可选参数构建我的 SQL...这样您的查询处理 null 并且您可以从该查询中获得更多信息..所以我实际上会使用@RequestParam(required = false) 但是搜索端点应该真正返回一个数组,而不是单个项目 将空值传递给查询和let the query handle it。 好吧,在这种情况下,如果存在的话,它应该只是一个订单。 如果它是一个搜索端点(它是),它应该返回一个结果数组......它也被称为getOrders,它也会让人们相信它返回多个......跨度> 【参考方案1】:

为了尽量减少方法中的参数数量,您可以将查询参数定义为类的字段:

@Data
public class SearchOrderCriteria 
    private String itemId;
    private String orderId;
    private String deliveryId;
    private String packetId;

然后在您的控制器方法中接收此类的实例:

@GetMapping(path = "/order/items", produces = "application/json")
public ResponseEntity<OrderInfo> getOrder(SearchOrderCriteria searchCriteria) 
    OrderInfo order = orderService.findOrder(searchCriteria)
    return ResponseEntity.ok(order);

而且,在你的服务中,为了避免一堆if-else,你可以使用query by example:

public OrderInfo findOrder(SearchOrderCriteria searchCriteria) 

    OrderInfo order = new OrderInfo();
    order.setItemId(searchCriteria.getItemId());
    order.setOrderId(searchCriteria.getOrderId());
    order.setDeliveryId(searchCriteria.getDeliveryId());
    order.setPacketId(searchCriteria.getPacketId());

    Example<OrderInfo> example = Example.of(order);
    return orderRepository.findOne(example);

【讨论】:

我喜欢这种方法,但也许我还不了解整个情况:) 我仍然很困惑如何根据获取端点中的查询参数来填充 SearchCriteria 中的字段? @lapadets 查看 Spring Data 文档以获取有关 query by example 工作原理的更多详细信息。 谢谢!你的回答对我帮助很大!【参考方案2】:

我的小建议是避免使用过于通用的 API,例如您可以将映射拆分为多个端点,例如:/order/delivery/itemId/itemId 和 /order/delivery/deliveryId/deliveryId 和 /order /delivery/packetId/packetId 并处理您需要在客户端调用的。

【讨论】:

感谢您的建议 - 我通常也会这样做,但问题是它需要是一个端点并且不会从多个客户端调用。 在这种情况下,您可以将所有参数放入非空值的列表过滤器中,例如:List listWithoutNulls = list.stream() .filter(Objects::nonNull) .collect(Collectors.toList ()) 然后继续策略模式以选择需要调用的数据源

以上是关于您将如何使用 Spring Boot 处理仅具有可选查询参数的 REST API?的主要内容,如果未能解决你的问题,请参考以下文章