我们在写代码中,前端请求后端接口传输参数必须要进行参数校验:

Contoller

  • 校验请求参数的合法性,包括:必填项校验,数据格式校验,比如:是否是符合一定的日期格式,等。

Service

  • 校验的是业务规则相关的内容,比如:课程已经审核通过所以提交失败。
SpringBoot提供了JSR-303的支持,它就是spring-boot-starter-validation。
它的底层使用HibernateValidator,Hibernate Validator是Bean Validation 的参考实现。
所以,我们准备在Controller层使用spring-boot-starter-validation完成对请求参数的基本合法性进行校验。

maven

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
现在准备对内容管理模块添加课程接口进行参数校验,如下接口:
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody AddCourseDto addCourseDto){
		return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}
此接口使用AddCourseDto模型对象接收参数,所以进入AddCourseDto类,在属性上添加校验规则。
/**
 * @description 添加课程dto
 * @author anqin
 * @date 2022/9/7 17:40
 * @version 1.0
 */
@Data
public class AddCourseDto {

 @NotEmpty(message = "课程名称不能为空",groups = GroupCheck.Insert.class)
 @NotEmpty(message = "课程名称不能为空",groups = GroupCheck.Update.class)
 private String name;

 @NotEmpty(message = "适用人群不能为空")
 @Size(message = "适用人群内容过少",min = 10)
 private String users;

 @NotEmpty(message = "课程分类不能为空")
 private String mt;

 @NotEmpty(message = "课程分类不能为空")
 private String st;

 @NotEmpty(message = "收费规则不能为空")
 private String charge;
}

上边用到了@NotEmpty和@Size两个注解,@NotEmpty表示属性不能为空,@Size表示限制属性内容的长短。
在javax.validation.constraints包下有很多这样的校验注解:

规则如下:

定义好校验规则还需要开启校验,在controller方法中添加@Validated注解,如下:
@PostMapping("/course")
public CourseBaseInfoDto createCourseBase(@RequestBody @Validated AddCourseDto addCourseDto){
	return courseBaseInfoService.createCourseBase(companyId,addCourseDto);
}
如果校验出错Spring会抛出MethodArgumentNotValidException异常,我们需要在统一异常处理器中捕获异常,解析出异常信息。
/**
 * @author anqin
 * @version 1.0
 * @description 全局异常处理器
 * @date 2022/9/6 11:29
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * @ResponseBody 返回json
     */
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse doValidException(MethodArgumentNotValidException argumentNotValidException) {
        BindingResult bindingResult = argumentNotValidException.getBindingResult();

        StringBuffer errMsg = new StringBuffer();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        fieldErrors.forEach(error -> {
            errMsg.append(error.getDefaultMessage()).append(",");
        });
        log.error(errMsg.toString());
        return new RestErrorResponse(errMsg.toString());
    }
}

/**
 * @author anqin
 * @version 1.0
 * @description 通用错误信息
 * @date 2022/9/6 11:29
 */
public enum CommonError {
    
    UNKNOWN_ERROR("执行过程异常,请重试。"),
    PARAMS_ERROR("非法参数"),
    OBJECT_NULL("对象为空"),
    QUERY_NULL("查询结果为空"),
    REQUEST_NULL("请求参数为空");

    private String errMessage;

    public String getErrMessage() {
        return errMessage;
    }

    private CommonError(String errMessage) {
        this.errMessage = errMessage;
    }
}
执行测试,接口响应结果如下:
{
	"errMessage": "课程名称不能为空 课程分类不能为空 课程分类不能为空 适用人群内容过少 "
}

分组校验

有时候在同一个属性上设置一个校验规则不能满足要求,比如:订单编号由系统生成,在添加订单时要求订单编号为空,在更新 订单时要求订单编写不能为空。
此时就用到了分组校验,同一个属性定义多个校验规则属于不同的分组,比如:
添加订单定义@NULL规则属于insert分组,更新订单定义@NotEmpty规则属于update分组,insert和update是分组的名称,是可以修改的。
如:
/**
 * @ClassName: GroupCheck
 * @author: anqin
 * @Description: 分组校验
 * @date: 2023/1/31 21:49
 */
public class GroupCheck {

    public interface Insert{};

    public interface Update{};

    public interface Delete{};
}

下边在定义校验规则时指定分组:
 @NotEmpty(message = "课程名称不能为空",groups = GroupCheck.Insert.class)
 @NotEmpty(message = "课程名称不能为空",groups = GroupCheck.Update.class)
 private String name;
在Controller方法中启动校验规则指定要使用的分组名:

@MonitorExecTime(description = "新增课程接口")
@RequestMapping(value = "/course", method = RequestMethod.POST)
public CourseBaseInfoDto createCourseBase(@RequestBody
                                          @Validated(value = GroupCheck.Insert.class) AddCourseDto addCourseDto) {

       return courseBaseService.createCourseBase(addCourseDto);
}