SpringMvc,Springboot统一校验自定义异常全局异常处理

Posted 诺浅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringMvc,Springboot统一校验自定义异常全局异常处理相关的知识,希望对你有一定的参考价值。

引入jar包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

写一个DTO用来接收客户端传送的参数

@Data
public class AuditListDto 
    @ApiModelProperty(value = "提交人ID", required = true)
    @NotNull
    private Long commitPersonnelId;

    @ApiModelProperty(value = "提交人名称", required = true)
    @NotNull
    private String commitPersonnelName;

    @ApiModelProperty(value = "巡检设备表ID集合", required = true)
    @NotEmpty
    private List<Long> inspectionEquipmentIds;

在Controller的方法里面上加上@Validated注解

@PostMapping("submitAccept")
@ApiOperation(value = "06.巡检数据提交验收", notes = "巡检数据提交验收")
@ApiOperationSupport(order = 22)
public ResultData<Void> submitAccept(@Validated @RequestBody AuditListDto conditionDto) 
    service.submitAccept(conditionDto);
    return ResultData.success();

定义一个统一异常拦截器

package com.yxk.config;

import com.yxk.utils.ResultData;
import com.yxk.utils.ResultDataException;
import com.yxk.utils.Utils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;

@RestControllerAdvice
public class GlobalExceptionHandler 

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class.getName());

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultData<?> handleException(HttpRequestMethodNotSupportedException e) 
        return ResultData.failed(ResultData.NOT_SUPPORTED_HTTP_METHOD, "不支持' " + e.getMethod() + "'请求");
    

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResultData<?> handleException(MissingServletRequestParameterException e) 
        return ResultData.failed(ResultData.MISSING_HTTP_PARAMETER,
                "缺少必要参数" + e.getParameterName() + ", 类型:" + e.getParameterType());
    

    @ExceptionHandler(BindException.class)
    public ResultData<?> handleException(BindException e) 
        return ResultData.failed(ResultData.BIND_ERROR, e.getFieldErrors().get(0).getDefaultMessage());
    

    @ExceptionHandler(ResultDataException.class)
    public ResultData<?> handleResultDataException(ResultDataException e) 
        logger.info("", e);
        return e.toResultData();
    

    @ExceptionHandler(DataIntegrityViolationException.class) // uk/fk
    public ResultData<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) 
        final String delFK = "Cannot delete or update a parent row: a foreign key constraint fails";
        final String saveFK = "Cannot add or update a child row: a foreign key constraint fails";
        final String UK = "Duplicate entry";
        final String truncation = "Data truncation";//

        logger.error("", e);

        if (e instanceof DuplicateKeyException) 
            // pk (not to here, except direct insert by native sql)
            return ResultData.failed(ResultData.INTEGRITY_VIOLATION, "重复的id");
        

        Throwable root = ExceptionUtils.getRootCause(e);
        if (root != null) 
            String err = Utils.findStr(root.getMessage(), UK);
            if (err != null)  // uk
                return ResultData.failed(ResultData.DUPLICATE_ENTRY, "重复数据,不能保存", err);
            
            err = Utils.findStr(root.getMessage(), delFK);
            if (err != null) // fk: Cannot delete or update a parent row: a foreign key constraint fails
                return ResultData.failed(ResultData.DUPLICATE_ENTRY, "数据已被使用,不能删除", err);
            
            err = Utils.findStr(root.getMessage(), saveFK);
            if (err != null) // fk: Cannot add or update a child row: a foreign key constraint fails
                return ResultData.failed(ResultData.DUPLICATE_ENTRY, "无效的引用id,不能保存", err);
            
            err = Utils.findStr(root.getMessage(), truncation);
            if (err != null) // Data truncation: Out of range value for column 'fee'
                return ResultData.failed(ResultData.DUPLICATE_ENTRY, "数据截断(值超出范围)", err);
            
            return ResultData.failed(ResultData.DUPLICATE_ENTRY, "数据不完整", root.getMessage());
        

        return ResultData.failed(ResultData.INTEGRITY_VIOLATION, "数据不完整");
    

    /**
     * 处理@Validated参数校验失败异常
     *
     * @param exception 异常类
     * @return 响应
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultData<?> validExceptionHandler(MethodArgumentNotValidException exception) 
        BindingResult result = exception.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder();
        if (result.hasErrors()) 
            List<ObjectError> errors = result.getAllErrors();
            if (errors != null) 
                StringBuilder finalStringBuilder = stringBuilder;
                errors.forEach(p -> 
                    FieldError fieldError = (FieldError) p;
                    logger.warn("Bad Request Parameters: dto entity [],field [],message []", fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
                    finalStringBuilder.append(String.format("%s[%s]%s", p.getObjectName(), ((FieldError) p).getField(), p.getDefaultMessage())).append(",");
                );
            
            if (stringBuilder.length() > 0) 
                stringBuilder = stringBuilder.deleteCharAt(stringBuilder.length() - 1);
            
        

        return ResultData.failed(ResultData.VALIDATE_ERROR, stringBuilder.toString());
    

    @ExceptionHandler(RuntimeException.class)
    public ResultData<?> handlerUnknown(RuntimeException e) 
        logger.error("", e);
        Throwable root = ExceptionUtils.getRootCause(e);
        if (root != null) 
            return ResultData.failed(ResultData.UNKNOWN_ERROR, "未知错误", root.getMessage());
        
        return ResultData.failed(ResultData.UNKNOWN_ERROR, "未知错误", e.getMessage());

    


自定义异常

public class ResultDataException extends RuntimeException 
    private static final long serialVersionUID = -9041090610418135434L;

    public static ResultDataException of(int code, String message) 
        return new ResultDataException(code, message);
    

    public static ResultDataException of(int code, String message, Object data) 
        return new ResultDataException(code, message, null, data);
    

    public static ResultDataException duplicateKey(String message) 
        return new ResultDataException(ResultData.DUPLICATE_KEY, message);
    

    public static ResultDataException notfound(String message) 
        return new ResultDataException(ResultData.NOT_FOUND, message);
    

    public static ResultDataException notfound(Long id) 
        return new ResultDataException(ResultData.NOT_FOUND, "记录不存在: " + id);
    

    public static ResultDataException validateRequired(String field) 
        return new ResultDataException(ResultData.VALIDATE_ERROR, field + " 不能为空");
    

    public static ResultDataException validteInvalidFormat(String field, String value) 
        return new ResultDataException(ResultData.VALIDATE_ERROR, field + " 无效格式: " + value);
    

    public static ResultDataException validteTooLong(String field, int maxLength, String value) 
        return new ResultDataException(ResultData.VALIDATE_ERROR,
                String.format("[%s]超长(%d, %d): %s", field, maxLength, value.length(), value));
    

    private final int code;
    private final String internalError;
    private final Object data;

    public ResultDataException(int code, String message) 
        this(code, message, null);
    

    public ResultDataException(int code, String message, String internalError) 
        this(code, message, internalError, null);
    

    public ResultDataException(int code, String message, String internalError, Object data) 
        super(message);
        this.code = code;
        this.internalError = internalError;
        this.data = data;
    

    @SuppressWarnings("unchecked")
    public <T> ResultData<T> toResultData() 
        return (ResultData<T>) ResultData.of(code, super.getMessage(), internalError, data);
    


public class ResultData<T> 

    public static <T> ResultData<T> of(int code, String message, T data) 
        return of(code, message, null, data);
    

    public static <T> ResultData<T> of(int code, String message, String internalError, T data) 
        ResultData<T> ret = new ResultData<>();
        ret.code = code;
        ret.msg = message;
        ret.internalError = internalError;
        ret.data = data;
        return ret;
    

    public static <T> ResultList<T> ofList(int code, String message, String internalError, List<T> data) 
        ResultList<T> result = of(data, data == null ? 0 : data.size());
        result.setCode(code);
        result.setMsg(message);
        result.setInternalError(internalError);
        return result;
    

    public static <T> ResultData<T> of(T data) 
        return of(SUCCESS, null, data);
    

    public static <T> ResultList<T> of(List<T> data) 
        return of(data, data.size());
    

    public static <T> ResultList<T> of(List<T> data, long total) 
        ResultList<T> ret = new ResultList<>();
        ret.setData(data);
        ret.setTotal(total);
        return ret;
    

    public static <T> ResultData<T> success(T data) 
        return of(SUCCESS, null, data);
    

    // success (no ret data)
    public static <T> ResultData<T> success() 
        return of(SUCCESS, null, null);
    

    public static <T> ResultData<T> success(String message, T data) // success with message
        return of(SUCCESS, message, data);
    
    //

    public static <T> ResultData<T> failed(int code, String message) 
        return failed(code, message, null);
    

    public static <T> ResultData<T> failed(int code, String message, String internalError) 
        return of(code, message, internalError, null);
    

    public static <T> ResultData<T> unknown(String message) 
        return of以上是关于SpringMvc,Springboot统一校验自定义异常全局异常处理的主要内容,如果未能解决你的问题,请参考以下文章

springBoot参数联合校验,自定义分组校验

SpringBoot自定义校验注解

SpringBoot Validation参数校验 详解自定义注解规则和分组校验

SpringBoot分组校验及自定义校验注解

重学SpringBoot系列之统一全局异常处理

springboot的统一处理