JSR303校验
一、背景
二、常用注解
注解 | 说明 |
---|---|
@Null | 被注释的元素必须为null |
@NotNull | 被注释的元素不能为null |
@AssertTrue | 被注释的元素必须为true |
@AssertFalse | 被注释的元素必须为false |
@Min(value) | 被注释的元素必须是一个数字,其值必须>=指定的最小值 |
@Max(value) | 被注释的元素必须是一个数字,其值必须<=指定的最大值 |
@DecimalMin(value) | 被注释的元素必须是一个数字,其值必须>=指定的最小值 |
@DecimalMax(value) | 被注释的元素必须是一个数字,其值必须<=指定的最大值 |
@Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
@Digits(integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
@Past | 被注释的元素必须是一个过去的日期 |
@Future | 被注释的元素必须是一个将来的日期 |
@Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Hibernate Validation 是JSR 303 的一个参考实现,除支持所有的标准的校验注解之外,
它还支持以下的扩展注解:
注解 | 说明 |
---|---|
被注释的元素必须是一个电子邮箱地址 | |
@Length | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串必须非空 |
@Range | 被注释的元素必须在合适的范围内 |
三、上手使用
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2.示例
2.1 @Validated注解方式
package com.fly.springbootmybatis.domain;
import com.fly.springbootmybatis.constants.RegularConstants;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
@Data
public class YS implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(name = "ckTime", value = "抽卡时间")
@Pattern(regexp = RegularConstants.YYYY_MM_DD_HH_mm_SS,message = "查询时间格式错误!")
private String ckTime;
@ApiModelProperty(name = "name", value = "名称")
private String name;
@ApiModelProperty(name = "type", value = "类型")
@NotNull(message = "类型不能为空!")
private String type;
@ApiModelProperty(name = "start", value = "稀有度")
@Range(min = 3L,max = 5L)
private String start;
@ApiModelProperty(hidden = true)
private Integer zcount;
@ApiModelProperty(hidden = true)
private Integer bcount;
}
package com.fly.springbootmybatis.constants;
/**
* 常用正则表达式
*/
public class RegularConstants {
/**
* 日期:yyyy-MM-dd HH:mm:ss
*/
public static final String YYYY_MM_DD_HH_mm_SS = "^(((20[0-3][0-9]-(0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|(20[0-3][0-9]-(0[2469]|11)-(0[1-9]|[12][0-9]|30))) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$";
/**
* 手机号(国内)
*/
public static final String PHONE_CHINA = "0?(13|14|15|17|18)[0-9]{9}";
/**
* 身份证号
*/
public static final String ID_NUMBER = "\\d{17}[\\d|x]|\\d{15}";
}
package com.fly.springbootmybatis.controller;
import com.fly.springbootmybatis.domain.YS;
import com.fly.springbootmybatis.service.YSService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "原神查询类",produces = MediaType.APPLICATION_JSON_VALUE)
@RestController
@RequestMapping("/YS")
public class YSController {
@Autowired
private YSService ysService;
@ApiOperation(value = "查询抽卡数据",produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping
public List<YS> findAll(@Validated YS ys){
return ysService.findAll(ys);
}
}
2.2 不加注解方式
package com.fly.springbootmybatis.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.groups.Default;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Slf4j
public class ValidatorUtils {
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* 使用指定分组
*
* @param bean 被校验的bean
* @param groups 分组
* @return
*/
public static <T> Map<String, StringBuilder> validate(T bean, Class<?>... groups) {
Field[] fields = bean.getClass().getDeclaredFields();
int length = fields.length;
Map<String, StringBuilder> errorMap = new HashMap<>(length);
if (groups == null) {
groups = new Class[]{Default.class};
}
Set<ConstraintViolation<T>> set = validator.validate(bean, groups);
if (CollectionUtils.isEmpty(set)) {
return null;
}
String property;
for (ConstraintViolation<T> c : set) {
// 循环获取错误信息
property = c.getPropertyPath().toString();
if (errorMap.get(property) != null) {
errorMap.get(property).append(",").append(c.getMessage());
} else {
StringBuilder sb = new StringBuilder();
sb.append(c.getMessage());
errorMap.put(property, sb);
}
}
return errorMap;
}
/**
* 使用指定分组
*
* @param bean 被校验的bean
* @param property 被校验的字段
* @param groups 分组
* @return
*/
public static <T> Map<String, StringBuilder> validateProperty(T bean, String property, Class<?>... groups) {
Field[] fields = bean.getClass().getDeclaredFields();
int length = fields.length;
Map<String, StringBuilder> errorPropertyMap = new HashMap<>(length);
if (groups == null) {
groups = new Class[]{Default.class};
}
Set<ConstraintViolation<T>> set = validator.validateProperty(bean, property, groups);
if (CollectionUtils.isEmpty(set)) {
return null;
}
for (ConstraintViolation<T> c : set) {
// 循环获取错误信息
if (errorPropertyMap.get(property) != null) {
errorPropertyMap.get(property).append(",").append(c.getMessage());
} else {
StringBuilder sb = new StringBuilder();
sb.append(c.getMessage());
errorPropertyMap.put(property, sb);
}
}
return errorPropertyMap;
}
}
package com.fly.springbootmybatis.controller;
import cn.hutool.core.util.ObjectUtil;
import com.fly.springbootmybatis.enums.ResultEnum;
import com.fly.springbootmybatis.exception.ValidateException;
import com.fly.springbootmybatis.utils.ValidatorUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class BaseController {
protected <T> void validate(T bean, Class<?>... groups) {
Map<String, StringBuilder> errorMap = ValidatorUtils.validate(bean);
if (ObjectUtil.isNotNull(errorMap)) {
log.info("------参数校验失败:{}", errorMap);
throw new ValidateException(ResultEnum.ILLEGAL_PARAMETER.getCode(), ResultEnum.ILLEGAL_PARAMETER.getMsg(), errorMap);
}
}
}
package com.fly.springbootmybatis.exception;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ValidateException extends RuntimeException {
/**
* 状态码
*/
private Integer code;
/**
* 错误提示
*/
private String message;
/**
* 错误详细信息
*/
private Object data;
}
/**
* 处理JSR303验证异常
*
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class, ValidateException.class})
public AjaxResult handleVaildException(Exception e) {
BindingResult bindingResult = null;
if (e instanceof MethodArgumentNotValidException) {
bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
} else if (e instanceof BindException) {
bindingResult = ((BindException) e).getBindingResult();
} else if (e instanceof ValidateException) {
return AjaxResult.error(((ValidateException) e).getCode(),e.getMessage(),((ValidateException) e).getData());
}
Map<String, String> errorMap = new HashMap<>(16);
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
fieldErrors.forEach(error -> errorMap.put(error.getField(), error.getDefaultMessage()));
log.info("------参数校验失败:{}", errorMap);
return AjaxResult.error(ResultEnum.ILLEGAL_PARAMETER.getCode(),ResultEnum.ILLEGAL_PARAMETER.getMsg(),errorMap);
}
package com.fly.springbootmybatis.controller;
import com.fly.springbootmybatis.domain.YS;
import com.fly.springbootmybatis.service.YSService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "原神查询类",produces = MediaType.APPLICATION_JSON_VALUE)
@RestController
@RequestMapping("/YS")
public class YSController extends BaseController {
@Autowired
private YSService ysService;
@ApiOperation(value = "查询抽卡数据",produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping
public List<YS> findAll(YS ys){
super.validate(ys);
return ysService.findAll(ys);
}
}
3.效果
4.优化显示
显然这种显示体验并不好,而且也无从知道参数错误,所以这里使用全局异常处理来返回自定义返回结果集
package com.fly.springbootmybatis.exception;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fly.springbootmybatis.domain.AjaxResult;
import com.fly.springbootmybatis.enums.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 全局处理异常类
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理业务异常
*
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(value = BusinessException.class)
public AjaxResult businessExceptionHandler(BusinessException e) {
log.error(e.getMessage());
return AjaxResult.error(e.getMessage());
}
/**
* 处理JSR303验证异常
*
* @param e
* @return
*/
@ResponseBody
@ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class, ValidateException.class})
public AjaxResult handleVaildException(Exception e) {
BindingResult bindingResult = null;
if (e instanceof MethodArgumentNotValidException) {
bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
} else if (e instanceof BindException) {
bindingResult = ((BindException) e).getBindingResult();
} else if (e instanceof ValidateException) {
return AjaxResult.error(((ValidateException) e).getCode(),e.getMessage(),((ValidateException) e).getData());
}
Map<String, String> errorMap = new HashMap<>(16);
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
fieldErrors.forEach(error -> errorMap.put(error.getField(), error.getDefaultMessage()));
log.info("------参数校验失败:{}", errorMap);
return AjaxResult.error(ResultEnum.ILLEGAL_PARAMETER.getCode(),ResultEnum.ILLEGAL_PARAMETER.getMsg(),errorMap);
}
/**
* 处理其他异常
*
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
public ModelAndView exceptionHandler(Exception e) {
log.error(e.getMessage());
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("5xx");
return modelAndView;
}
}
package com.fly.springbootmybatis.domain;
import com.fly.springbootmybatis.enums.ResultEnum;
import lombok.*;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class AjaxResult implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String msg;
private Integer count;
private Object data;
private AjaxResult() {
}
public AjaxResult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public AjaxResult(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public static AjaxResult success(Integer count, Object data) {
return new AjaxResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMsg(), count, data);
}
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(ResultEnum.SUCCESS.getCode(), msg, data);
}
public static AjaxResult error(String msg) {
return new AjaxResult(ResultEnum.FAIL.getCode(), msg);
}
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(ResultEnum.FAIL.getCode(), msg, data);
}
public static AjaxResult error(Integer code, String msg, Object data) {
return new AjaxResult(code, msg, data);
}
}
package com.fly.springbootmybatis.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum ResultEnum {
SUCCESS(200, "成功!"),
FAIL(100,"失败!"),
FILE_MAX(110,"上传文件过大!"),
ILLEGAL_PARAMETER(400,"非法参数!"),
NO_PERMISSION(403, "权限不足!"),
NO_AUTH(401, "未登录!"),
NOT_FOUND(404, "未找到该资源!"),
NOT_REQUEST_METHOD(405,"该请求方式不支持!"),
INTERNAL_SERVER_ERROR(500, "服务器异常请联系管理员!");
/**
* 错误码
*/
private Integer code;
/**
* 错误信息
*/
private String msg;
}
优化后的展示情况
最后附上查询成功数据