SpringBoot restful返回值+统一异常处理+参数校验

restful返回值

自定义错误类型

新建枚举类,写入错误码及错误提示

/**
 * 接口返回错误码,错误提示
 * 参考 https://blog.csdn.net/alisonyu/article/details/82833413
 */
public enum ResultConsts {
    SYSERR(-1,"系统错误"),
    OK(0,"请求成功"),
    ILLGAL_PARAM(40001,"参数无效"),
    ;

    private Integer code;
    private String msg;

    ResultConsts(Integer code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode(){
        return code;
    }

    public void setCode(Integer code){
        this.code = code;
    }

    public String getMsg(){
        return msg;
    }

    public void setMsg(String msg){
        this.msg = msg;
    }

} 

Restful接口返回值拦截

/**
 * 捕获所有Rest接口返回值
 * springboot-Rest接口返回统一格式数据
 * 参考 https://blog.csdn.net/alisonyu/article/details/82833413
 */
@ControllerAdvice(annotations = RestController.class)
public class ResultHandler implements ResponseBodyAdvice{

    private ThreadLocal<ObjectMapper> mapperThreadLocal = ThreadLocal.withInitial(ObjectMapper::new);

    private static final Class[] annos={
            RequestMapping.class,
            GetMapping.class,
            PostMapping.class,
            DeleteMapping.class,
            PutMapping.class
    };

    /**
     * 对所有RestController的接口方法进行拦截
     * @param returnType
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType){
        AnnotatedElement element = returnType.getAnnotatedElement();
        return Arrays.stream(annos).anyMatch(anno -> anno.isAnnotation() && element.isAnnotationPresent(anno));
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        Object out;
        ObjectMapper mapper = mapperThreadLocal.get();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        /*根据返回数据类型,封装返回值*/
        if (body instanceof ResultConsts){
            return ResultUtils.getResult((ResultConsts) body);
        }
        else if(body == null){
            return ResultUtils.getResult(ResultConsts.OK);
        }
        else{
            return ResultUtils.getResult(ResultConsts.OK, body);
        }
    }
} 

统一异常处理

自定义异常

/**
 * 自定义异常,对应ResultConsts
 */
public class DefaultException extends RuntimeException{
    //异常码
    private Integer code;
    //异常信息
    private String msg;

    public DefaultException(Integer code, String msg){
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode(){
        return code;
    }

    public void setCode(Integer code){
        this.code = code;
    }

    public String getMsg(){
        return msg;
    }

    public void setMsg(String msg){
        this.msg = msg;
    }
} 

统一异常捕获

使用@ControllerAdvice进行注解

/**
 * 全局异常捕获
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * Exception,默认异常
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Object ExceptionHandler(Exception ex){
        return ResultUtils.getResult(ResultConsts.SYSERR);
    }

    /**
     * DefaultException,自定义异常
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = DefaultException.class)
    public Object ExceptionHandler(DefaultException ex){
        return ResultUtils.getResult(ex.getCode(),ex.getMsg());
    }

    /**
     * ValidationException,参数校验异常
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = ValidationException.class)
    public Object validationExceptionHandler(ValidationException ex){
        ConstraintViolationException ex1 = (ConstraintViolationException) ex;
        Set<ConstraintViolation<?>> violations = ex1.getConstraintViolations();
        String errorInfo = "";
        for(ConstraintViolation<?> item : violations){
            errorInfo = errorInfo + " " + item.getMessage();
        }
        return ResultUtils.getResult(ResultConsts.ILLGAL_PARAM.getCode(), ResultConsts.ILLGAL_PARAM.getMsg() + errorInfo);
    }
} 

示例

@RestController
@RequestMapping(value="/demo")
public class DemoController {
    @GetMapping(value = "/err")
    public String err(){
        throw new DefaultException(0,"Error");
    }

    @GetMapping(value = "/error")
    public Integer error(){
        return 0/0;
    }
}

参数校验

hibernate validator配置

/**
 * Hibernate 参数校验
 * 参考 https://www.jianshu.com/p/aa8b3163b30a
 */
@Configuration
@EnableAutoConfiguration
public class ValidatorConfig {
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor(){
        MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
        postProcessor.setValidator(validator());
        return postProcessor;
    }

    /**
     * 使用快速校验模式,一旦出错立刻返回
     * @return
     */
    @Bean
    public Validator validator(){
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
                .addProperty("hibernate.validator.fail_fast", "true")
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();
        return validator;
    }
} 

使用hibernate validator注解

Bean Validation 中内置的 constraint
@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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

示例

使用@Validated进行注解

@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController {

    @GetMapping(value = "/email")
    public String email(@Email(message = "邮件格式错误") String email){
        return "ok";
    }
}

自定义校验

自定义校验注解

/**
 * 自定义校验注解
 * 参考 https://www.jianshu.com/p/320cee90391f
 */
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER,ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
//java标准校验注解,validateBy指定校验的类
@Constraint(validatedBy = PwdValidator.class)
@Documented
public @interface PwdCheck {

    //校验注解中必须实现以下三个属性
    String message() default ValidatorConsts.ILLGAL_PWD;

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
} 

校验实现

/**
 * 实现ConstraintValidator接口,第一个泛型指定用于标注验证的注解,第二个泛型指定倍标注值得类型
 * 不需要用@component等注解将验证类加入容器。spring会自动将此类加入容器
 */
public class PwdValidator implements ConstraintValidator<PwdCheck,String> {

    //在这个类中可以使用Spring @Autowire注解注入任何需要的对象

    private final static String regExp = "^(?=.*[a-zA-Z0-9].*)(?=.*[a-zA-Z\\W].*)(?=.*[0-9\\W].*).{8,20}$";

    /**
     * 校验器初始化
     * @param pwdCheck
     */
    @Override
    public void initialize(PwdCheck pwdCheck) {

    }

    /**
     * 校验方法
     * @param str 待校验的值
     * @param constraintValidatorContext
     * @return 返回true代表校验成功,false代表校验失败
     */
    @Override
    public boolean isValid(String str, ConstraintValidatorContext constraintValidatorContext) {
        if(StringUtils.isBlank(str)){
            return false;
        }
        Pattern p = Pattern.compile(regExp);
        Matcher m = p.matcher(str);
        return m.matches();
    }
} 

校验异常捕获

参考统一异常捕获

/**
 * 全局异常捕获
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    /**
     * ValidationException,参数校验异常
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = ValidationException.class)
    public Object validationExceptionHandler(ValidationException ex){
        ConstraintViolationException ex1 = (ConstraintViolationException) ex;
        Set<ConstraintViolation<?>> violations = ex1.getConstraintViolations();
        String errorInfo = "";
        for(ConstraintViolation<?> item : violations){
            errorInfo = errorInfo + " " + item.getMessage();
        }
        return ResultUtils.getResult(ResultConsts.ILLGAL_PARAM.getCode(), ResultConsts.ILLGAL_PARAM.getMsg() + errorInfo);
    }
} 

参数校验

@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController { 
    @GetMapping(value = "/pwd")
    public String pwd(@PwdCheck(message = "密码格式错误" ) String pwd)  {
        return "ok";
    }
    
    @PostMapping(value = "post")
    public String post(@PwdCheck(message = "密码格式错误" ) String pwd){
        return "ok";
    }
}

实体类校验

参数定义

/**
 * 实体类参数
 */
public class UserInfo {
    @NotBlank(message = ValidatorConsts.NOTBLANK_ID)
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
} 

示例

@Validated
@RestController
@RequestMapping(value="/demo")
public class DemoController {
    @PostMapping(value = "/userinfo")
    public String userInfo(@Valid UserInfo userInfo, BindingResult result){
        return "ok";
    }
}

代码示例

https://github.com/sleetdream/spring-cloud-demo

参考

https://www.cnblogs.com/mr-yang-localhost/p/7812038.html#_label3
https://www.cnblogs.com/gdwkong/p/8969963.html
https://blog.csdn.net/alisonyu/article/details/82833413
https://www.cnblogs.com/NeverCtrl-C/p/8185576.html
https://blog.csdn.net/hutaodsb931211/article/details/78777947
https://blog.csdn.net/u013815546/article/details/77248003
https://www.cnblogs.com/magicalSam/p/7198420.html
https://blog.csdn.net/qq_33996921/article/details/79568456
https://www.jianshu.com/p/aa8b3163b30a
https://blog.csdn.net/fred_lzy/article/details/81099699
https://blog.csdn.net/qq_40325734/article/details/81782955
https://www.jianshu.com/p/320cee90391f
https://blog.csdn.net/matengbing/article/details/83188833

猜你喜欢

转载自blog.csdn.net/sleetdream/article/details/88788328