如何将@RequestParam注解的所有字段收集到一个对象中

问题描述 投票:0回答:3

我想将所有查询参数收集到 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 中的我的字段上设置给定的请求参数。

请指教。

spring-boot spring-mvc
3个回答
13
投票

有人之前已经使用了此功能,以便您可以执行以下操作。但不幸的是,由于不活动响应,它被拒绝了:

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
    没有明确注释。


0
投票

基于此问题: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()
    );
  }

-2
投票

说实话,对于 spring-boot 中已经存在的功能来说,这似乎需要付出很大的努力。您可以从

PagingAndSortingRepository
扩展存储库,并在调用集合资源时添加分页。

或者您可以编写自定义查询方法(或覆盖现有的查询方法)并添加以下内容:

Page<Person> findByFirstname(String firstname, Pageable pageable);

这样 Spring Boot 会自动将您想要的所有参数添加到请求中。

© www.soinside.com 2019 - 2024. All rights reserved.