我想将所有查询参数收集到 pojo 中并对字段执行额外的验证。
我读过,我可以简单地创建一个对象,spring-boot 将自动在其上设置这些请求参数。
@GetMaping
public ResponseEntity<?> listEntities(@RequestParam(value = "page-number", defaultValue = "0") @Min(0) Integer pageNumber,
@RequestParam(value = "page-size", defaultValue = "100") @Min(1) Integer pageSize ... )
我正在考虑创建一个名为
RequestParamsDTO
的类,其中我的查询参数负责分页。
但是为了在
RequestParamsDTO
上设置这些字段,我必须将请求参数的名称与字段名称相匹配。但它不会是一个有效的变量名称:page-size
。
必须有一些解决方法,类似于
@RequestParam
的 value 属性,可以在 DTO 中的我的字段上设置给定的请求参数。
请指教。
有人之前已经使用了此功能,以便您可以执行以下操作。但不幸的是,由于不活动响应,它被拒绝了:
public class RequestParamsDTO{
@RequestParam(value="page-number",defaultValue="0")
@Min(0)
private Integer pageNumber;
@RequestParam(value = "page-size", defaultValue = "100")
@Min(1)
Integer pageSize
}
您可以做的最相似的事情是使用它的
@ModelAttribute
它将解析以下 orders 中的参数:
- 如果已使用模型添加,则来自模型。
- 使用 @SessionAttributes 来自 HTTP 会话。
- 来自通过转换器传递的 URI 路径变量(请参阅下一个示例)。
- 来自默认构造函数的调用。
- 使用与 Servlet 请求参数匹配的参数调用“主构造函数”。参数名称通过 JavaBeans @ConstructorProperties 或通过字节码中运行时保留的参数名称确定。
这意味着
RequestParamsDTO
不能没有任何默认构造函数(没有参数的构造函数)。它应该有一个“主构造函数”,您可以使用@ConstructorProperties
来定义哪些请求参数映射到构造函数参数:
public class RequestParamsDTO{
@Min(0)
Integer pageNumber;
@Min(1)
Integer pageSize;
@ConstructorProperties({"page-number","page-size"})
public RequestParamsDTO(Integer pageNumber, Integer pageSize) {
this.pageNumber = pageNumber != null ? pageNumber : 0;
this.pageSize = pageSize != null ? pageSize : 100;
}
}
控制器方法变为:
@GetMaping
public ResponseEntity<?> listEntities(@Valid RequestParamsDTO request){
}
备注:
@RequestParam
的defaultValue
没有等效的注解,所以需要在构造函数中手动实现。如果控制器方法参数与 this 中的值不匹配,它将解析为
@ModelAttribute
,即使 @ModelAttribute
没有明确注释。基于此问题:https://github.com/spring-projects/spring-framework/issues/23618
Spring 不允许在字段级别使用
@RequestParam
。因此,我通过以下方式实现了我的解决方案。
这是整个项目中多个地方使用的请求参数。
@GetMapping("/me/unread")
public PageableResult<NotificationResponse> getUnreadOperatorNotifications(
@Min(value = 1, message = "page must be equal or bigger than 1") @RequestParam int page,
@Min(value = 1, message = "perPage must be equal or bigger than 1") @RequestParam int perPage,
@AuthenticationPrincipal CustomUserDetails user
) {
return notificationService.findUnreadNotifications(page, perPage, user.getId());
}
为了替换逻辑,首先创建
PaginationRequest
记录(也可以是类)
public record PaginationRequest(int page, int perPage) {
}
然后创建一个名为
PaginationRequestParamResolver
的自定义方法参数解析器
public class PaginationRequestParamResolver implements HandlerMethodArgumentResolver {
@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory
) throws Exception {
int page = Integer.parseInt(Objects.requireNonNull(webRequest.getParameter("page")));
int perPage = Integer.parseInt(Objects.requireNonNull(webRequest.getParameter("perPage")));
if (page < 1) {
throw ResponseException.badRequest("page must be equal or bigger than 1");
}
if (perPage < 1) {
throw ResponseException.badRequest("perPage must be equal or bigger than 1");
}
return new PaginationRequest(page, perPage);
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return PaginationRequest.class.isAssignableFrom(parameter.getParameterType());
}
}
最后,创建一个
WebMvcConfiguration
类来添加上述参数解析器。
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new PaginationRequestParamResolver());
}
}
就是这样。现在您可以用自定义记录替换逻辑,如下所述:
@GetMapping("/me/unread")
public PageableResult<NotificationResponse> getUnreadOperatorNotifications(
PaginationRequest request,
@AuthenticationPrincipal CustomUserDetails user
) {
return notificationService.findUnreadNotifications(
request.page(),
request.perPage(),
user.getId()
);
}
说实话,对于 spring-boot 中已经存在的功能来说,这似乎需要付出很大的努力。您可以从
PagingAndSortingRepository
扩展存储库,并在调用集合资源时添加分页。
或者您可以编写自定义查询方法(或覆盖现有的查询方法)并添加以下内容:
Page<Person> findByFirstname(String firstname, Pageable pageable);
这样 Spring Boot 会自动将您想要的所有参数添加到请求中。