环境:SpringBoot3.2.5
1. 简介
web请求的数据绑定涉及将请求参数绑定到模型对象。默认情况下,请求参数可以绑定到模型对象的任何公共属性,这意味着恶意客户端可以为模型对象中存在的属性提供额外的值,但这些属性并不期望被设置。所以在进行模型对象设计时需要仔细考虑,避免不合理的设计代理不必要的麻烦或安全问题。如下示例:
// 模型数据public class User {private Integer age ;private String name ;private String password ;private String email ;// getters, setters}// 接口定义;这里只希望更新年龄字段age和邮箱email2个字段@GetMapping("/updateBasicInfo")public R updateAgeAndEmail(User user) {// 更新操作userMapper.updateUserInfo(user) ;return R.success("更新成功") ;}// UserMapper#updateAgeAndEmail更新操作<update id="updateUserInfo" parameterType="com.pack.User">UPDATE t_user<set><if test="name != null and name != ''">name = #{name},</if><if test="email != null and email != ''">email = #{email},</if><if test="password != null and password != ''">password = #{password},</if><if test="age != null">age = #{age},</if></set>WHERE id = #{id}</update>
在上面的示例代码中,我们通过User接收客户端的请求参数信息,注意这里参数前并没有添加@RequestBody注解,Spring也是可以根据你请求的参数进行数据绑定。
我们期望的是只更新age和email,但客户端请求中可能包含其他不需要更新的字段,那么直接在User实体上接收所有请求参数可能会导致意外的字段更新。
2. 解决办法
2.1 使用DTO
创建一个只包含你需要更新的字段的DTO类。这个DTO类将用于接收客户端的请求数据。然后,在你的服务层中,你可以将DTO的数据复制到你的User实体中,只更新必要的字段。
public class UserBasicDTO {private Integer age ;private String email ;// getters and setters}
Controller接口接收参数时使用UserBasicDTO对象。
public R updateAgeAndEmail(UserBasicDTO dto)
这种方式挺好就是需要再定义一个类,有没有不定义类的方式呢?有,也是今天本篇文章的重点要介绍的。
2.2 使用@InitBinder注解
设置允许绑定的属性
通过WebDataBinder 对象设置 allowedFields 模式(区分大小写),以防止设置意外属性,如下示例:
public void initBinder(WebDataBinder binder) {binder.setAllowedFields("age", "email") ;}
通过如上的设置后,不管你传入的有哪些参数,都只会绑定age和email字段。相反你也可以设置不允许绑定的字段
binder.setDisallowedFields("name", "password") ;
不过,"允许"配置比 "不允许"配置更受欢迎,因为它更明确,不容易出错。
设置构造函数绑定
默认情况下,构造函数和属性绑定都会被使用。如果只想使用构造函数绑定,可以通过 @InitBinder方法在控制器中本地或通过@ControllerAdvice 全局设置 WebDataBinder上的 declarativeBinding 标志。打开该标记可确保只使用构造函数绑定,而不使用属性绑定,除非配置了 allowedFields 模式,如下示例:
public void initBinder(WebDataBinder binder) {// 设置构造函数绑定binder.setDeclarativeBinding(true) ;}
接下来还需要修改User实体对象
public static class User {private Integer age ;private String name ;private String password ;private String email ;public User(Integer age, String email) {this.age = age ;this.email = email ;}}
请求的参数只会根据这里的构造函数进行绑定;如果有多个构造函数呢?
当定义多个构造函数后,程序将抛出错误。

@InitBinder其它高级配置
配置必须的字段
public void initBinder(WebDataBinder binder) {binder.setRequiredFields("id") ;}
虽然实体中没有定义id,但是我们要求请求的参数中必须有id字段,否则将抛出下面异常。

添加验证器
你也可以添加自定义的参数验证器
public void initBinder(WebDataBinder binder) {binder.addValidators(new UserValidator()) ;}// 自定义验证器public class UserValidator implements Validator {public boolean supports(Class<?> clazz) {return User.class.isAssignableFrom(clazz) ;}public void validate(Object target, Errors errors) {User user = (User) target ;if (user.getAge() < 0) {errors.reject("user.error.age", "年龄错误") ;}}}// 接口添加注解("/model")public User model(@Valid User user)
当有错误,将抛出如下异常。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
SpringBoot异步任务@Async你真的会用吗?这6点细节你知道吗?
优雅重构Spring Boot代码,我用这6种策略消灭if else
强大!实时监控SpringBoot运行时状态及应用运行时信息(数据库, Redis,MQ等)
警惕!SpringBoot错误发布事件,造成死锁Deadlock
强大的Spring Data 还能这么玩 Redis,太强了



