天天看点

Validation 全局异常处理1. Validation 全局异常处理代码2. ValidatorUtils 工具类

目录

  • 1. Validation 全局异常处理代码
  • 2. ValidatorUtils 工具类
    • 2.1 使用方法
    • 2.2 自定义异常和全局异常处理

1. Validation 全局异常处理代码

package com.android.handler;

import com.android.common.domain.ResponseVo;
import com.android.common.exception.NotLoginException;
import com.android.common.exception.ParamNotIllegalException;
import com.android.common.sysenum.HttpResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

import static com.android.constant.StringContants.SEPARATE;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * MethodArgumentNotValidException异常:
     *      当 @Validated 修饰 java 对象时,如 (@RequestBody @Validated LoginDto loginDto)
     *      此时当 javabean 中的属性验证失败时,会抛出所有不符合属性的msg
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseVo handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        BindingResult bindingResult = ex.getBindingResult();
        // 日志将所有错误信息完全打印出来
        StringBuilder errorLog = new StringBuilder("校验失败——");
        // 定义返回给前端的内容,只返回给前端最先报错的信息
        String msg = bindingResult.getFieldError().getDefaultMessage();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errorLog.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        log.debug(SEPARATE + errorLog + SEPARATE);
        return ResponseVo.errorResult(400, msg);
    }

    /**
     * ConstraintViolationException异常:
     *      当 @Validated 修饰单个参数时(此时 @Validated 在类上而不是在方法中),
     *      会抛出ConstraintViolationException异常。
     *      
     * @param ex
     * @return
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseVo handleConstraintViolationException(ConstraintViolationException ex) {
        String message = ex.getMessage();
        /*
        处理格式。
        ex.getMessage() 格式为:方法名.参数名: message
        如:sendEmail.email: 邮箱格式错误,表示是  sendEmail 方法的 email 参数格式校验错误。
         */
        String msg = message.substring(message.indexOf(":") + 1).trim();
        return ResponseVo.errorResult(400, msg);
    }

	/**
     * 处理异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    public ResponseVo validationExceptionHandler(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<String> errorList = new ArrayList<>();
        String msg = bindingResult.getFieldError().getDefaultMessage();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errorList.add(fieldError.getDefaultMessage());
        }
        log.debug(String.join(",", errorList));
        return ResponseVo.errorResult(400, msg);
    }
}
           

2. ValidatorUtils 工具类

该工具类用于解决 @Validated / @Valid 注解 全局异常处理较为麻烦的问题:

package com.android.util;

import com.android.common.exception.ParamNotIllegalException;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

public class ValidatorUtils {
    private static Validator validator;
 
    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
 
    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws ParamNotIllegalException 校验不通过,则报ApiException异常
     */
    public static void validateEntity(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            constraintViolations.forEach(e -> {
	            // 自定义异常,抛出后可以直接在全局异常处理中接收
                throw new ParamNotIllegalException(e.getMessage());
            });
        }
    }
}
           

2.1 使用方法

在控制器中使用 ValidateEntity方法取代 @Validated 注解,因为 @Validated 注解抛出的异常有多种情况,处理起来较为麻烦,而 ValidateEntity 可以抛出自定义异常,处理起来较为容易。

/**
 * 邮箱登录
 * @param emailLoginDto
 * @return
 */
@ApiOperation("邮箱登录")
@PostMapping("/emailLogin")
public ResponseVo emailLogin(@RequestBody EmailLoginDto emailLoginDto) {
    log.debug(SEPARATE + "emailLogin方法执行" + SEPARATE);
    
	// 参数中不写 @Validated,在此处进行验证,抛出自定义异常,在全局异常处理中接收
    ValidatorUtils.validateEntity(emailLoginDto);

    return userService.emailLogin(emailLoginDto);
}
           

EmailLoginDto类:

package com.android.domain.dto;

import io.swagger.annotations.ApiModelProperty;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;

public class EmailLoginDto implements Serializable {
    /**
     * 序列化版本号
     */
    private static final long serialVersionUID = 1253859525433214940L;

    /**
     * 用户邮箱,抛出自定义异常中的 message 就是 @NotBlank 注解中的 message
     */
    @NotBlank(message = "邮箱不能为空")
    @Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "邮箱格式错误")
    @ApiModelProperty(value = "用户邮箱", required = true)
    private String email;

    /**
     * 邮箱验证码
     */
    @NotBlank(message = "验证码不能为空")
    @ApiModelProperty(value = "邮箱验证码", required = true)
    private String code;
}

           

2.2 自定义异常和全局异常处理

自定义异常:

package com.android.common.exception;

public class ParamNotIllegalException extends RuntimeException{
    public ParamNotIllegalException(String message) {
        super(message);
    }
}
           

全局异常处理:

package com.android.handler;

import com.android.common.domain.ResponseVo;
import com.android.common.exception.NotLoginException;
import com.android.common.exception.ParamNotIllegalException;
import com.android.common.sysenum.HttpResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import static com.android.constant.StringContants.SEPARATE;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 处理ParamNotIllegalException参数不合法异常
     * 该处理器是专门用于处理validation的400异常的
     * @param e 捕获的异常
     * @return responseVo错误封装错误信息
     */
    @ExceptionHandler(ParamNotIllegalException.class)
    public ResponseVo paramNotIllegalExceptionHandler(Exception e) {
        log.debug(SEPARATE + "paramNotIllegalException异常处理器执行" + SEPARATE);
        return ResponseVo.errorResult(400, e.getMessage());
    }

    @ExceptionHandler(NotLoginException.class)
    public ResponseVo notLoginException(Exception e) {
        log.debug(SEPARATE + "notLoginException异常处理器执行" + SEPARATE);
        return ResponseVo.errorResult(HttpResponseEnum.NEED_LOGIN);
    }

    /**
     * 处理所有异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseVo exception(Exception e) {
        e.printStackTrace();
        return ResponseVo.errorResult(HttpResponseEnum.SYSTEM_ERROR);
    }
}
           

如果有什么改进之处,感谢大家留言!