栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot校验(validation)+全局处理异常

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot校验(validation)+全局处理异常

SpringBoot校验(validation)+全局处理异常 SpringBoot校验(validation)

hibernate-validator中有很多非常简单好用的校验注解,例如NotNull,@NotEmpty,@Min,@Max,@Email,@PositiveOrZero等等。这些注解能解决我们大部分的数据校验问题。如下所示:

package com.nobody.dto;

import lombok.Data;

import javax.validation.constraints.*;

@Data
public class UserDTO {

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Min(value = 18, message = "年龄不能小于18")
    private int age;

	@NotEmpty(message = "邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
}
二 自定义参数校验器

但是,hibernate-validator中的这些注解不一定能满足我们全部的需求,我们想校验的逻辑比这复杂。所以,我们可以自定义自己的参数校验器。

首先引入依赖是必不可少的。
在springBoot中可以直接引用


    org.springframework.boot
    spring-boot-starter-validation

这里面正是包含了我们真正需要的hibernate-validator依赖


  org.hibernate.validator
  hibernate-validator
  6.2.0.Final
  compile

向他提供的注解一样我们需要自定义自己的注解。

再建一个validator包来放我们的自定义注解类

package com.zry.seckill.validator;

import com.zry.seckill.vo.IsMobilevalidator;

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.Target;

import static java.lang.annotation.RetentionPolicy.RUNTIME;


@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RUNTIME)
@documented
@Constraint(validatedBy = {IsMobilevalidator.class})//指定我们的校验类,这里面实现了我们需要的具体校验方法
public @interface IsMobile {
   
    boolean required() default true;  
    
  
    String message() default "手机号码格式错误";
    
   
    Class[] groups() default {};
    
   
    Class[] payload() default {};

}

接下来我们完成校验类 它实现ConstraintValidator接口
实现里面的方法。

package com.zry.seckill.vo;

import com.zry.seckill.utils.ValidatorUtil;
import com.zry.seckill.validator.IsMobile;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;


public class IsMobilevalidator implements ConstraintValidator {

    private boolean required = false;// 是否强制校验
    @Override
    public void initialize(IsMobile constraintAnnotation) {
        this.required = constraintAnnotation.required();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
		//我这里调用了自定义的工具类
        return ValidatorUtil.isMobile(value);
    }
}

最后我们需要在Controller层加上 @Valid 注解才能启用注解校验。

当然还有另一种@Validated,和@Valid有所不同,这里不做详述。

Spring Validation验证框架对参数的验证机制提供了@Validated(Spring’s
JSR-303规范,是标准JSR-303的一个变种),javax提供了@Valid(标准JSR-303规范),配合BindingResult可以直接提供参数验证结果。

通过以上几个步骤,我们自定义的校验注解就完成了。

如果参数校验不通过,会抛出MethodArgumentNotValidException异常,我们全局处理下然后返回给接口。

SpringBoot全局处理异常 为什么要处理异常?

在日常开发中,为了不抛出异常堆栈信息给前端页面,每次编写Controller层代码都要尽可能的catch住所有service层、dao层等异常,代码耦合性较高,且不美观,不利于后期维护。
为解决该问题,我们将Controller层异常信息统一封装处理,且能区分对待Controller层方法返回给前端的String、Map、JSONObject、ModelAndView等结果类型。

简单介绍

我们涉及到 这三个注解。

  • @RestControllerAdvice
  • @ControllerAdvice + @ExceptionHandler

对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller
Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。

3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

@RestControllerAdvice与@ControllerAdvice的区别

@RestControllerAdvice与@ControllerAdvice的区别就和@RestController与@Controller的区别类似,@RestControllerAdvice注解包含了@ControllerAdvice注解和@ResponseBody注解。
(参考https://www.pianshen.com/article/6221932106/)
@RestControllerAdvice源码中有@ControllerAdvice注解和@ResponseBody注解,当自定义类加@RestControllerAdvice注解时,方法自动返回json数据,每个方法无需再添加@ResponseBody注解

我们这次提供的方案是@RestControllerAdvice + @ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

实现步骤

我们先建一个excepttion包来放我们的全局异常类。

自定义异常类
package com.zry.seckill.exception;

import com.zry.seckill.vo.RespBeanEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class GlobalException extends RuntimeException{
    private RespBeanEnum respBeanEnum;
}

自定义全局异常处理
package com.zry.seckill.exception;

import com.zry.seckill.vo.RespBean;
import com.zry.seckill.vo.RespBeanEnum;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;



@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)//处理哪些异常
    public RespBean ExceptionHandler(Exception e){
    
        if(e instanceof GlobalException){//如果是咱们之前自定义的异常
            GlobalException ex = (GlobalException) e;
            return RespBean.error(ex.getRespBeanEnum());
        }else if(e instanceof BindException){ //如果异常是绑定异常(比如没有通过参数校验注解抛出的异常)
            BindException be = (BindException) e;
            RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
            respBean.setMessage("参数校验异常" + be.getBindingResult().getAllErrors().get(0).getDefaultMessage());
            return respBean;
        }
        return RespBean.error(RespBeanEnum.ERROR);
    
    }
	
}

这里我们就已经实现了对Controller层的全局异常处理。

下面是对上面方法中出现RespBeanEnum和RespBean的一些说明

GlobalExceptionHandler() 类中都是我们自己编写的处理异常的方法。
我这里的ExceptionHandler()方法只是个例子。
其中RespBeanEnum是我自定义的枚举类。它主要是为RespBean类服务。RespBean相当于统一返回实体类
用来返回结果信息。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RespBean {
    private long code;
    private String message;
    private Object obj;

    
    public static RespBean success(){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(),null);
    }

    
    public static RespBean success(Object obj){
        return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(),obj);
    }
    
    public static RespBean error(RespBeanEnum respBeanEnum){
        return new RespBean(respBeanEnum.getCode(),respBeanEnum.getMessage(),null);
    }
    
    public static RespBean error(RespBeanEnum respBeanEnum,Object obj){
        return new RespBean(respBeanEnum.getCode(),respBeanEnum.getMessage(),obj);
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/691511.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号