SpringBoot参数校验
本文共 10,131 字,预计阅读时间 34 分钟
无论在做接口或还是前后端交互时,进行必要的参数校验是很重要的,可有效避免垃圾数据。
这里使用Spring Validation校验器进行说明,导入依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
1.RequestBody类型校验
一般主要是POST请求使用RequestBody进行参数接收,具体是使用@RequestBody+对象方式接收。下面一一说明:
1.1单个对象
实体类(需要注意的是,@NotBlank只能验证String类型的参数)
package com.zxh.bootdemo0705.entity; import com.zxh.bootdemo0705.validator.DateVerify; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; @Data @AllArgsConstructor @NoArgsConstructor public class User { @NotBlank(message = "姓名不能为空") private String name; @NotBlank(message = "性别不能为空") private String sex; @NotBlank(message = "出生日期不能为空") @DateVerify(dateFormat = "yyyy-MM-dd") private String birthDay; }
日期验证注解
package com.zxh.bootdemo0705.validator; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * 日期类型校验注解 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {DateVerifyValidator.class}) // 指定自定义的校验器 public @interface DateVerify { // 提示信息 String message() default "日期格式不正确"; // 日期格式 String dateFormat() default "yyyy-MM-dd HH:mm:ss"; /** * 必须包含以下两个属性 * 否则会报错 error msg: contains Constraint annotation, but does not contain a groups parameter. * * @return */ Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
日期验证器,验证日期格式
package com.zxh.bootdemo0705.validator; import cn.hutool.core.util.StrUtil; import com.zxh.bootdemo0705.util.DateUtil; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * 日期类型校验器 */ public class DateVerifyValidator implements ConstraintValidator<DateVerify, String> { private String dateFormat; @Override public void initialize(DateVerify obj) { dateFormat = obj.dateFormat(); } /** * 对参数进行验证 * * @param value 修饰字段的值 * @param context 上下文 * @return true:验证通过, false:验证不通过 */ @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (StrUtil.isNotEmpty(value)) { return DateUtil.isValidDate(value, dateFormat); } return true; } }
验证格式的工具类
package com.zxh.bootdemo0705.util; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { /** * 验证字符串是否日期格式 * * @param str 日期字符串 * @param dateFormat 格式 * @return */ public static boolean isValidDate(String str, String dateFormat) { boolean convertSuccess = true; if (null != str && null != dateFormat && str.length() == dateFormat.length()) { SimpleDateFormat format = new SimpleDateFormat(dateFormat); try { // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01 format.setLenient(false); format.parse(str); } catch (ParseException e) { convertSuccess = false; } } else { convertSuccess = false; } return convertSuccess; } }
全局异常处理,主要是最后两个异常处理来捕获参数不合法所抛出的异常
package com.zxh.bootdemo0705.exception; import com.zxh.bootdemo0705.entity.RsData; import lombok.extern.slf4j.Slf4j; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.CollectionUtils; import org.springframework.validation.DataBinder; import org.springframework.validation.ObjectError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.RestController; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringJoiner; @ControllerAdvice @Slf4j @RestController public class InterfaceExceptionHandler { private String paramValid = "参数不合法:"; // 将Spring DataBinder配置为使用直接字段访问 @InitBinder private void activateDirectFieldAccess(DataBinder dataBinder) { dataBinder.initDirectFieldAccess(); } /** * 方法不被允许 * * @param e * @return */ @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) public RsData HttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { log.error("请求方法异常!原因是:{}", e); return RsData.fail("方法不被允许"); } /** * 请求体参数为空 * * @param e * @return */ @ExceptionHandler(value = HttpMessageNotReadableException.class) public RsData HttpMessageNotReadableException(HttpMessageNotReadableException e) { log.error("参数不合法!原因是:{}", e.getMessage()); return RsData.fail(paramValid + "请求体为空"); } /** * 运行时异常 * * @param e * @return */ @ExceptionHandler(value = RuntimeException.class) public RsData RuntimeException(RuntimeException e) { log.error("服务器异常!原因是:{}", e); return RsData.fail("服务器开小差了,请稍后再试"); } /** * 用来处理validation异常,捕获get请求Param参数校验异常信息 * * @param ex * @return */ @ExceptionHandler(ConstraintViolationException.class) public RsData resolveConstraintViolationException(ConstraintViolationException ex) { Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations(); if (!CollectionUtils.isEmpty(constraintViolations)) { StringJoiner msgJoiner = new StringJoiner(","); Set<String> errors = new HashSet<>(constraintViolations.size() / 2); for (ConstraintViolation constraintViolation : constraintViolations) { errors.add(constraintViolation.getMessage()); } for (String error : errors) { msgJoiner.add(error); } String errorMessage = msgJoiner.toString(); return RsData.fail(paramValid + errorMessage); } return RsData.fail(paramValid + ex.getMessage()); } /** * 用来处理validation异常,捕获post请求Body实体属性参数校验异常信息 * * @param ex * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) public RsData resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex) { List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors(); if (!CollectionUtils.isEmpty(objectErrors)) { StringJoiner msgJoiner = new StringJoiner(","); Set<String> errors = new HashSet<>(objectErrors.size() / 2); for (ObjectError objectError : objectErrors) { errors.add(objectError.getDefaultMessage()); } for (String error : errors) { msgJoiner.add(error); } String errorMessage = msgJoiner.toString(); return RsData.fail(paramValid + errorMessage); } return RsData.fail(paramValid + ex.getMessage()); } }
返回对象实体类(可根据实际情况编写)
package com.zxh.bootdemo0705.entity; import lombok.Data; @Data public class RsData { private static final String SUCCESS = "0"; private static final String FAIL = "1"; private String code; private Object result; private String message; public static RsData success(String msg) { RsData rs = new RsData(); rs.setCode(SUCCESS); rs.setMessage(msg); return rs; } public static RsData success(String msg, Object result) { RsData rs = new RsData(); rs.setCode(SUCCESS); rs.setMessage(msg); rs.setResult(result); return rs; } public static RsData error(String msg) { RsData rs = new RsData(); rs.setCode(FAIL); rs.setMessage(msg); return rs; } public static RsData error(String msg, Object result) { RsData rs = new RsData(); rs.setCode(FAIL); rs.setMessage(msg); rs.setResult(result); return rs; } public static RsData fail(String result) { RsData rs = new RsData(); rs.setCode(FAIL); rs.setMessage("失败"); rs.setResult(result); return rs; } }
测试接口,在使用@RequestBody注入对象时添加注解@Valid进行校验,若不加则不进行验证
@PostMapping("/add") public String add(@RequestBody @Valid User user) { System.out.println(user); return "添加成功"; }
测试:验证不合法的参数
测试:输入正确的参数
常见的约束注解如下表:
注解 | 功能 |
@NotNull | 验证字符串。不能为null,可以是空字符串 |
@NotBlank | 验证字符串。不能为null或空字符串 |
@NotEmpty | 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于"" |
@Pattern | 必须满足指定的正则表达式 |
@Length | 长度必须在指定范围内 |
@Range | 值必须在指定范围内 |
@Max | 最大不得超过此最大值 |
@Min | 最大不得小于此最小值 |
1.2对象集合
添加集合验证
package com.zxh.bootdemo0705.validator; import lombok.experimental.Delegate; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; /*** * 由于Validator只能验证对象合法性,故数组方式需自定义处理,这是方式一 * 也可直接在controller类上加@Validated注解 * @param <E> */ public class ListValidation<E> implements List<E> { @Delegate @Valid public List<E> list = new ArrayList<>(); @Override public String toString() { return list.toString(); } }
添加接口
@PostMapping("/add2") public String add(@RequestBody @Validated ListValidation<User> userList) { System.out.println(userList); return "添加成功"; }
注意,这是使用的是ListValidation进行接收,还配合使用了@Validated注解。两者都可以用来校验参数,但区别如下:
@Valid是用Hibernate validation校验机制,而@Validated是用Spring Validator校验机制。
@Valid可以用在方法、构造函数、方法参数和成员属性(字段)上,支持嵌套检测。@Validated可以用在类型、方法和方法参数上,但是不能用在成员属性(字段)上,不支持嵌套检测。
测试结果如下:
就是这么简单,你学废了吗?感觉有用的话,给笔者点个赞吧 !
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)