天天看點

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);
    }
}
           

如果有什麼改進之處,感謝大家留言!