Spring中将@RequestParam绑定到对象(转)

译文:https://blog.csdn.net/dnc8371/article/details/106810876/

译文作者:dnc8371

来源:CSDN

原文:https://www.javacodegeeks.com/2018/10/how-bind-requestparam-object-spring.html

您是否在请求映射方法中用@RequestParam注释了多个参数,并认为它不可读?

当请求中需要一个或两个输入参数时,注释看起来非常简单,但是当列表变长时,您可能会感到不知所措。

您不能在对象内部使用@RequestParam批注,但这并不意味着您没有其他解决方案。 在本文中,我将向您展示如何用object替换多个@RequestParams 。

1. @RequestParams的列表太长

无论是控制器还是其他类,我都相信您同意很难读取一长串方法参数 。 另外,如果参数类型相同,则更容易出错。

诸如Checkstyle之类的静态代码分析工具可以检测方法中的大量输入,因为它被广泛认为是一种不好的做法。

通常,将一组参数一起传递到应用程序的不同层。 这样的组通常可以形成一个对象 ,您要做的就是提取它并给它起一个适当的名称 。

让我们看一下用于搜索某些产品的示例GET端点:

@RestController
@RequestMapping("/products")
class ProductController {
 
   //...
 
   @GetMapping
   List<Product> searchProducts(@RequestParam String query,
                                @RequestParam(required = false, defaultValue = "0") int offset,
                                @RequestParam(required = false, defaultValue = "10") int limit) {
       return productRepository.search(query, offset, limit);
   }
 
}

三个参数不是一个令人关注的数字,但它可以轻松增长。 例如,搜索通常包括排序顺序或一些其他过滤器。 在这种情况下,它们都被传递到数据访问层,因此它们似乎是参数对象提取的理想选择。

2. 将@RequestParam绑定到POJO

根本不用注解@RequestParams,如下,直接把POJO作为输入即可。

@GetMapping
List<Product> searchProducts(ProductCriteria productCriteria) {
   return productRepository.search(productCriteria);
}

POJO不需要任何其他注释。 它应具有与将与HTTP请求绑定的请求参数匹配的字段列表,标准的getter / setter和无参数的构造函数。

class ProductCriteria {
 
   private String query;
   private int offset;
   private int limit;
 
   ProductCriteria() {
   }
 
   public String getQuery() {
       return query;
   }
 
   public void setQuery(String query) {
       this.query = query;
   }
 
   // other getters/setters
 
}

验证POJO内部的请求参数

好的,但是我们不仅仅使用@RequestParam注释来绑定HTTP参数。 注释的另一个有用功能是可以根据需要标记给定参数。 如果请求中缺少参数,我们的端点可以拒绝它。

为了使用 POJO达到相同的效果(甚至更多!),我们可以使用bean验证 。 Java带有许多内置约束,但是如果需要,您总是创建一个自定义验证 。

让我们回到POJO并向字段添加一些验证规则。 如果只想模仿 @RequestParam(required = false) 的行为,则只需在必填字段上使用 @NotNull 批注 。

在许多情况下,使用@NotBlack代替@NotNull更有意义,因为它还涵盖了不需要的空字符串问题(长度为零的字符串)。

final class ProductCriteria {
 
   @NotBlank
   private String query;
   @Min(0)
   private int offset;
   @Min(1)
   private int limi;
 
   // ...
 
}

请注意:

添加字段的验证注释不足以使其起作用。

您还需要在控制器的方法中使用@Valid批注标记POJO参数。 这样,您通知Spring它应该在绑定步骤上执行验证。

@GetMapping
List<Product> searchProducts(@Valid ProductCriteria productCriteria) {
   // ...
}

POJO内部的默认请求参数值

@RequestParam批注的另一个有用的功能是能够在HTTP请求中未提供参数时定义默认值。

当我们拥有POJO时,不需要特殊的魔术。 您只需将默认值直接分配给字段。 当请求中缺少参数时,没有任何内容将覆盖预定义的值。

private int offset = 0;
private int limit = 10;

3. 多个对象

您没有被迫将所有HTTP参数放在单个对象中。 您可以在多个POJO中对参数进行分组。

为了说明这一点,让我们向端点添加排序条件。 首先,我们需要一个单独的对象。 就像之前一样,它具有一些验证约束。

final class SortCriteria {
 
   @NotNull
   private SortOrder order;
   @NotBlank
   private String sortAttribute;
 
   // constructor, getters/setters
 
}

在控制器中,只需将其添加为单独的输入参数即可。 请注意,@ Valid批注在每个应验证的参数上都是必需的。

@GetMapping
List<Product> searchProducts(@Valid ProductCriteria productCriteria, @Valid SortCriteria sortCriteria) {
   // ...
}

4. 嵌套对象

作为多个输入请求对象的替代,我们也可以使用组合。 参数绑定也适用于嵌套对象。

在下面,您可以找到一个示例,其中先前引入的排序条件已移至产品条件POJO。

要验证所有嵌套属性,应将@Valid批注添加到该字段。 请注意,如果该字段为null,Spring将不会验证其属性。 如果所有嵌套属性都是可选的,那可能是理想的解决方案。 如果不是,只需将@NotNull批注放在该嵌套对象字段上。

final class ProductCriteria {
 
   @NotNull
   @Valid
   private SortCriteria sort;
 
   // ...
 
}

HTTP参数必须使用点符号匹配字段名称。 在我们的情况下,它们应如下所示:

sort.order=ASC&sort.attribute=name

5. 不变的DTO

如今,您可以看到一种趋势,它倾向于使用不固定对象,而使用二传手来取代传统的POJO。

不可变的对象有很多好处(还有缺点……但是……)。 我认为,最大的一项是维护简单 。

您是否曾经在应用程序的数十个层中进行过跟踪,以了解哪些条件导致了对象的特定状态? 这个或那个字段在哪里改变了? 为什么要更新? setter方法的名称什么也没解释。 二传手没有任何意义。

考虑到创建Spring框架的事实,Spring强烈依赖POJO规范就不会让人感到惊讶。 然而,时代变了,旧的模式变成了反模式。

没有简单的方法可以使用参数化的构造函数将HTTP参数神奇地绑定到POJO。 非参数构造函数是不可避免的。 但是,我们可以将该构造函数设为私有 (但遗憾的是不能在嵌套对象中使用)并删除所有的setter。 从公众的角度来看,该对象将变得不可变。

默认情况下,Spring需要使用setter方法将HTTP参数绑定到字段。 幸运的是,可以重新配置绑定程序并使用直接字段访问(通过反射)。

为了为整个应用程序全局配置数据绑定器,您可以创建一个控制器建议组件。 您可以在以@InitBinder批注注释的方法内部更改绑定程序配置,该方法接受绑定程序作为输入。

@ControllerAdvice
class BindingControllerAdvice {
 
   @InitBinder
   public void initBinder(WebDataBinder binder) {
       binder.initDirectFieldAccess();
   }
 
}

创建该小类后,我们可以返回到POJO并从该类中删除所有setter方法,以使其变为只读状态以供公众使用。

final class ProductCriteria {
 
   @NotBlank
   private String query;
   @Min(0)
   private int offset = 0;
   @Min(1)
   private int limit = 10;
 
   private ProductCriteria() {
   }
 
   public String getQuery() {
       return query;
   }
 
   public int getOffset() {
       return offset;
   }
 
   public int getLimit() {
       return limit;
   }
 
}

重新启动您的应用程序,并使用HTTP请求的参数。 它应该像以前一样工作。

6. 结论

在本文中,您可以看到使用@RequestParam绑定在Spring MVC控制器中的HTTP请求参数可以轻松地替换为对多个属性进行分组的参数对象,仅不过是简单的POJO或可选的不可变DTO。

您可以在GitHub存储库中找到描述的样本 。 我希望所介绍的案例是不言自明的,但是如果有任何疑问或您想花两分钱,我强烈建议您将您的评论留在帖子下方。

翻译自: https://www.javacodegeeks.com/2018/10/how-bind-requestparam-object-spring.html

posted @ 2024-04-22 16:25  奋斗终生  Views(547)  Comments(0Edit  收藏  举报