从零开始学springboot

26. 从零开始学springboot-全局异常处理

2020-09-09  本文已影响0人  码哥说

前言

无论什么项目,异常处理和数据校验都显得尤其重要。作为一个开发,我们不应该不对数据检验就直接入库,我们也不应该傻乎乎的把乱糟糟的报错信息直接返回给用户。本章,我们就讲讲sprinboot的异常和数据校验处理。

异常处理流程

默认异常处理

默认情况下,sprinboot为两种情况提供了不同的响应方式。

默认异常处理原理简介

sprinboot默认提供了程序出错的结果映射路径“/error”。这个“/error”请求会在BasicErrorController类中处理
我们找到该类,简单看些核心实现


3.png

不难看出其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自浏览器,还是其它接口的调用,以此来决定返回页面视图还是 json 消息内容。

全局异常处理

以上介绍了springboot默认的异常处理机制,那么,问题来了,我们实际业务中肯定是需要统一的消息体的。所以,我们就需要实现ErrorController这个接口,来统一返回的消息体。
(PS:也可以直接继承BasicErrorController,该中已经有一个默认处理text/html的方法。如果你想添加一个新内容类型(如JSON)的处理程序,你需要添加一个具有@RequestMapping属性的方法,然后返回你自定义的实体类)。

package com.mrcoder.sbexceptionvalidator.controller;
import com.mrcoder.sbexceptionvalidator.common.model.ResponseInfo;
import com.mrcoder.sbexceptionvalidator.model.Person;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
@RestController
public class ExceptionValidatorController implements ErrorController {
    @RequestMapping("/error")
    public ResponseInfo handleError(HttpServletRequest request) {
        //获取statusCode:401,404,500
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        return ResponseInfo.fail(statusCode);
    }
    @Override
    public String getErrorPath() {
        return "/error";
    }
}

此时,再访问不存在的url你会发现返回的消息体已经是我们定义的了。
浏览器:


5.png

非浏览器:


4.png

自定义全局异常处理

上面我们提到ErrorController可对全局错误进行处理,但是有如下几点问题:

实际上,当出现错误,如获取值为空或出现异常时,我们并不希望用户看到异常的具体信息,而是希望对对应的错误和异常做相应提示
在MVC框架中很多时候会出现执行异常,那我们就需要加try/catch进行捕获,如果service层和controller层都加上,那就会造成代码冗余。

而@ControllerAdvice可对全局异常进行捕获,包括自定义异常(也就是我们自定定义的业务异常)
需要注意的是,@ControllerAdvice是应用于对springmvc中的控制器抛出的异常进行处理,而对于404/500这类的异常不会进入控制器处理的异常不起作用,所以404这类的还是要依靠实现ErrorController接口来处理。

我们定义一个错误码枚举类

package com.mrcoder.sbexceptionvalidator.common.status;
import com.mrcoder.sbexceptionvalidator.common.model.ResponseInfo;
/**
 * @Description: 系统错误类型枚举类
 */
public enum ErrorCodeEnum {
    SYSTEM_ERROR(ResponseInfo.FAIL_CODE, "系统异常,请稍后重试"),
    NO_AUTHORITY(ResponseInfo.FAIL_CODE, "无权访问"),
    PARAM_ERROR(ResponseInfo.FAIL_CODE, "参数非法"),
    NOT_LOG(ResponseInfo.FAIL_CODE, "当前用户未登录");
    private Integer errCode;
    private String errMsg;
    private ErrorCodeEnum(Integer errCode, String errMsg) {
        this.errCode = errCode;
        this.errMsg = errMsg;
    }
    public Integer getErrCode() {
        return errCode;
    }
    public String getErrMsg() {
        return errMsg;
    }
}

再定义一个业务异常类型

package com.mrcoder.sbexceptionvalidator.common.exception;


import com.mrcoder.sbexceptionvalidator.common.model.ResponseInfo;
import com.mrcoder.sbexceptionvalidator.common.status.ErrorCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * @Description: 全局业务异常类型
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = -6842004487143726249L;
    private Integer errCode;
    private String errMsg;
    public BusinessException(String errMsg) {
        super();
        this.errCode = ResponseInfo.FAIL_CODE;
        this.errMsg = errMsg;
    }
    public BusinessException(ErrorCodeEnum errorCodeEnum) {
        super();
        this.errCode = errorCodeEnum.getErrCode();
        this.errMsg = errorCodeEnum.getErrMsg();
    }
}

最后全局异常处理器

package com.mrcoder.sbexceptionvalidator.common.exception;


import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

import com.mrcoder.sbexceptionvalidator.common.model.ResponseInfo;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


/**
 * @Description: 全局异常捕获处理器
 */
@ControllerAdvice
public class GlobalExpectionHandler {
    /**
     * 异常捕获处理
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseInfo expectionHandler(HttpServletRequest request, Exception e) {
        if (e instanceof MethodArgumentNotValidException) { // JavaBean参数校验异常
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;
            List<ObjectError> allErrors = exception.getBindingResult().getAllErrors(); // 取出错误信息
            ObjectError error = allErrors.get(0); // 只返回第一个错误信息即可
            return ResponseInfo.fail(error.getDefaultMessage());
        } else if (e instanceof ConstraintViolationException) { // Controller方法参数校验异常
            // 错误异常
            String message = ((ConstraintViolationException) e).getConstraintViolations().iterator().next().getMessage();
            return ResponseInfo.fail(message);
        } else if (e instanceof BusinessException) {
            // 自定义业务异常
            BusinessException exception = (BusinessException) e;
            return ResponseInfo.fail(exception.getErrMsg());
        } else {
            // 系统异常,打印错误日志
            e.printStackTrace();
            return ResponseInfo.fail("系统异常,请稍后重试");
        }
    }
}

项目地址

https://github.com/MrCoderStack/SpringBootDemo/tree/master/sb-exception-validator

请关注我

上一篇 下一篇

猜你喜欢

热点阅读