1. jar包导入
//validator
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.7.Final</version>
</dependency>
2. 自定义注解
- 根据需要校验参数,这里校验用户的角色是否合法
package com.example.handlerinterceptor.annotation;
import com.example.handlerinterceptor.validator.IdentifyRoleValidator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
//配置校验类
@Constraint(validatedBy = IdentifyRoleValidator.class)
public @interface IdentifyRole {
String message() default "{javax.validation.constraints.IdentifyRole.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
3. 校验类
- 实现ConstraintValidator接口,指定具体的注解名称和属性类型
package com.example.handlerinterceptor.validator;
import com.example.handlerinterceptor.annotation.IdentifyRole;
import com.example.handlerinterceptor.enums.RoleEnum;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IdentifyRoleValidator implements ConstraintValidator<IdentifyRole, String> {
@Override
public void initialize(IdentifyRole constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
/**
* 通过枚举类校验用户角色
*/
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return RoleEnum.containsValue(value);
}
}
4. 角色枚举类
- 只能输入vip,customer,dealers三种角色
package com.example.handlerinterceptor.enums;
import java.util.Objects;
public enum RoleEnum {
VIP("vip"), CUSTOMER("customer"), DEALERS("dealers");
private String value;
RoleEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static boolean containsValue(String role) {
for (RoleEnum roleEnum : RoleEnum.values()) {
if (Objects.equals(role,roleEnum.getValue())) {
return true;
}
}
return false;
}
}
5. 校验注解排序
- 针对同时使用多个注解,而且需要指定校验顺序的场景
- 定义一个接口,然后通过@GroupSequence注解,按顺序添加排好顺序后的类
package com.example.handlerinterceptor.group;
public interface GroupA {}
package com.example.handlerinterceptor.group;
public interface GroupB {}
- 这里第一个为Default,即将没有指定顺序的参数校验注解作为第一个需要校验的注解,然后为GroupA和GroupB,可以根据需要继续添加GroupC、D…等
package com.example.handlerinterceptor.group;
import javax.validation.GroupSequence;
import javax.validation.groups.Default;
@GroupSequence({Default.class, GroupA.class, GroupB.class})
public interface Group {}
6. 实体类使用参数校验注解
package com.example.handlerinterceptor.sysuser.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.example.handlerinterceptor.annotation.IdentifyRole;
import com.example.handlerinterceptor.group.GroupA;
import com.example.handlerinterceptor.group.GroupB;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class SysUser extends Model<SysUser> {
@TableId(type = IdType.AUTO)
private Long userId;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
@TableLogic
@JsonIgnore
private Integer delFlag;
private String name;
private Integer age;
@NotBlank(message = "interest must not be blank")
private String interest;
@NotBlank(message = "role must not be blank", groups = {GroupA.class})
@IdentifyRole(message = "role is illegal", groups = {GroupB.class})
private String role;
/**
* 获取主键值
*
* @return 主键值
*/
@Override
public Serializable pkVal() {
return this.userId;
}
}
7. Controller具体使用
- 入参前添加@Validated({Group.class}),表示根据Group类中指定的顺序进行参数校验
@PostMapping
public AjaxResult insert(@RequestBody @Validated({Group.class}) SysUser sysUser) {
return AjaxResult.ok(this.sysUserService.save(sysUser));
}
8. 全局异常拦截
- 需要配合全局异常拦截,遇到非法入参,进行友好提示
package com.example.handlerinterceptor.handler;
import com.example.handlerinterceptor.dto.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
@RestControllerAdvice
public class GlobalExceptionHandler {
public class AjaxReult {
private Integer code;
private String msg;
public AjaxReult(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(ValidationException.class)
public AjaxReult handleValidationException(ValidationException e) {
LOGGER.error(e.getMessage(), e);
return new AjaxReult(1, e.getCause().getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
LOGGER.error(e.getMessage(), e);
return AjaxResult.error(e.getBindingResult().getFieldError().getDefaultMessage());
}
@ExceptionHandler(ConstraintViolationException.class)
public AjaxReult handleConstraintViolationException(ConstraintViolationException e) {
LOGGER.error(e.getMessage(), e);
return new AjaxReult(1, e.getMessage());
}
@ExceptionHandler(NoHandlerFoundException.class)
public AjaxReult handlerNoFoundException(Exception e) {
LOGGER.error(e.getMessage(), e);
return new AjaxReult(404, "路径不存在,请检查路径是否正确");
}
@ExceptionHandler(DuplicateKeyException.class)
public AjaxReult handleDuplicateKeyException(DuplicateKeyException e) {
LOGGER.error(e.getMessage(), e);
return new AjaxReult(1, "数据重复,请检查后提交");
}
@ExceptionHandler(Exception.class)
public AjaxReult handleException(Exception e) {
LOGGER.error(e.getMessage(), e);
return new AjaxReult(500, "系统繁忙,请稍后再试");
}
}
9. 请求接口
- 先输入一个非法的角色,抛出异常,被全局异常处理类捕获,根据注解中的message信息进行提示
- 输入一个空字符串,因为@NotBlank(message = “role must not be blank”, groups = {GroupA.class}),所有会优先判断是否为空,然后再判断是否非法
- 角色合法不为空,兴趣输入空字符串,会校验是否为空,这里因为在Group类中第一个指定了先校验Default
- 因为在接口入参处@Validated({Group.class})指定根据Group中的配置进行校验,如果不指定Default,则只有注解中groups指定了GroupA和GroupB的注解才会被校验
- 传入合法的参数,校验通过,请求成功