Spring Boot中Rest API过滤:合并相似查询接口为带Filter参数接口
合并Spring Boot相似接口的实现方案
这事儿我刚在项目里实践过,其实很容易落地,咱们一步步拆解来做:
1. 先定义筛选类型的枚举(推荐)
首先把你需要的filter可选值做成枚举,既避免硬编码字符串,还能方便后续的参数校验和逻辑分发:
public enum PostFilter { POPULAR, LATEST, DEFAULT; // 可选:把传入的字符串转为枚举,忽略大小写,兼容用户输入 public static PostFilter fromString(String filterStr) { if (filterStr == null || filterStr.isBlank()) { return DEFAULT; } try { return PostFilter.valueOf(filterStr.toUpperCase()); } catch (IllegalArgumentException e) { return DEFAULT; // 也可以抛出参数非法异常,根据你的业务容错需求来定 } } }
2. 修改控制器接口
把原来的三个接口合并成一个,用@RequestParam接收filter参数,并且设置默认值,这样不传filter的时候就走默认的帖子搜索逻辑:
@RestController @RequestMapping("/search") public class PostController { @Autowired private PostService postService; @GetMapping("/{topicId}") public ResponseEntity<List<Post>> getPosts( @PathVariable Long topicId, @RequestParam(value = "filter", defaultValue = "default") String filterStr) { // 把前端传入的字符串转成枚举类型 PostFilter filter = PostFilter.fromString(filterStr); // 根据筛选类型分发到对应的业务逻辑 List<Post> posts; switch (filter) { case POPULAR: posts = postService.getPopularPosts(topicId); break; case LATEST: posts = postService.getLatestPosts(topicId); break; default: posts = postService.searchPosts(topicId); break; } return ResponseEntity.ok(posts); } }
3. (可选)用策略模式优化逻辑分发
如果后续筛选类型越来越多,switch-case会变得臃肿难维护,这时候可以用策略模式来优化:
第一步:定义策略接口
public interface PostFilterStrategy { List<Post> getPosts(Long topicId); PostFilter getFilterType(); }
第二步:实现各个策略类
@Component public class PopularPostStrategy implements PostFilterStrategy { @Autowired private PostService postService; @Override public List<Post> getPosts(Long topicId) { return postService.getPopularPosts(topicId); } @Override public PostFilter getFilterType() { return PostFilter.POPULAR; } } // 同理实现LatestPostStrategy和DefaultPostStrategy
第三步:在控制器中注入策略映射
@RestController @RequestMapping("/search") public class PostController { private final Map<PostFilter, PostFilterStrategy> strategyMap; // 构造函数注入,Spring会自动把所有PostFilterStrategy实现类注入到map中 public PostController(List<PostFilterStrategy> strategies) { this.strategyMap = strategies.stream() .collect(Collectors.toMap(PostFilterStrategy::getFilterType, Function.identity())); } @GetMapping("/{topicId}") public ResponseEntity<List<Post>> getPosts( @PathVariable Long topicId, @RequestParam(value = "filter", defaultValue = "default") String filterStr) { PostFilter filter = PostFilter.fromString(filterStr); // 获取对应的策略,找不到就用默认策略兜底 PostFilterStrategy strategy = strategyMap.getOrDefault(filter, strategyMap.get(PostFilter.DEFAULT)); List<Post> posts = strategy.getPosts(topicId); return ResponseEntity.ok(posts); } }
4. 参数校验(推荐)
为了避免用户传入非法的filter值,你可以加上参数校验逻辑:
比如自定义校验注解:
// 自定义校验注解 @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PostFilterValidator.class) public @interface ValidPostFilter { String message() default "Invalid filter value. Allowed values: popular, latest, default"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } // 校验器实现 public class PostFilterValidator implements ConstraintValidator<ValidPostFilter, String> { private static final Set<String> ALLOWED_VALUES = Set.of("popular", "latest", "default"); @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null || value.isBlank()) { return true; // 允许空值,因为我们有默认值兜底 } return ALLOWED_VALUES.contains(value.toLowerCase()); } }
然后在控制器参数上加上@ValidPostFilter:
@GetMapping("/{topicId}") public ResponseEntity<List<Post>> getPosts( @PathVariable Long topicId, @RequestParam(value = "filter", defaultValue = "default") @ValidPostFilter String filterStr) { // 后续逻辑不变 }
这样一来,用户访问以下URL都会走同一个接口,并且根据filter参数分发到对应的逻辑:
GET /search/123→ 默认帖子搜索GET /search/123?filter=popular→ 热门帖子查询GET /search/123?filter=latest→ 最新帖子查询
内容的提问来源于stack exchange,提问作者ts178
相关产品推荐
相关产品推荐

