Springboot系列:全局异常处理

290 阅读3分钟

前言

关于异常处理,是我们后端开发中,可以说是十分重要,这关系到我们和前端是否能够有效沟通。

这样一个场景:

xx前端:xx后台,你接口报错了,快来看。

xx后端:怎么可能,报的什么错

xx前端:不知道,就是显示服务器错误。我怎么知道。

xx后端默默的打开日志,开始搜寻错误起来。经过一番努力,终于发现,原来是前台传参错误了。

接着后台就又开始跟前端交流,开始讲解如何传参。。。

此时时间已过去大半个小时了

就这样的场景,我们可以说是经常发生,如何我们能够有效的返回错误类型,以及提示信息。这样我们的开发效率就会大大提高了。这样是不是我们就有更多时间去学习(摸鱼)了

spring中异常处理3大注解

  • @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
  • @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开;其是对Controller层进行拦截
  • @ResponseStatus:可以将某种异常映射为HTTP状态码

在springboot中,用到的是aop容器思想,简单的概括就是,通过注解加入容器、然后注入到所需的地方。不明白的同学,应该去了解了解一下。

有了这3大注解,我们就可以处理我们的异常了。

代码实操

UnifyResponse.java //这里定义的是我们返回给前台的内容

public class UnifyResponse {
    private int code;

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public String getRequest() {
        return request;
    }

}

exception-code.properties // 配置错误类型,如下图

http.codes[30003] = 定义的错误类型1
http.codes[30005] = 定义的错误类型2

exception-code.properties 文件位置

image.png

ExceptionCodeConfiguration.java // 读取配置文件
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

@ConfigurationProperties(prefix = "http") // 配置文件键值前缀,http.codes即可用codes代替
@PropertySource(value = "classpath:config/exception-code.properties") // 配置文件路径
@Component // 加入容器
public class ExceptionCodeConfiguration {

    private Map<Integer, String> codes = new HashMap<>();
    
    public Map<Integer, String> getCodes() {
        return codes;
    }

    public void setCodes(Map<Integer, String> codes) {
        this.codes = codes;
    }

    public String getMessage(int code){
        String message = codes.get(code);
        return message;
    }
}

HttpException.java // 通用错误类型

public class HttpException extends RuntimeException {
    protected Integer code;

    public Integer getCode() {
        return code;
    }

    public Integer getHttpStatusCode() {
        return httpStatusCode;
    }

    protected Integer httpStatusCode = 500;
}
NotFoundException.java // 具体错误类型,可拓展其他具体错误

public class NotFoundException extends HttpException {
    public NotFoundException(int code){
        this.httpStatusCode = 404;
        this.code = code;
    }
}

GlobalExceptionAdvice.java // 全局错误处理文件

@ControllerAdvice // 可以捕获contrller的错误
public class GlobalExceptionAdvice {

    @Autowired
    private ExceptionCodeConfiguration codeConfiguration;

    @ExceptionHandler(value=Exception.class) // 通用错误类型
    @ResponseBody // 加上这个,才能返回给前端
    @ResponseStatus(code= HttpStatus.INTERNAL_SERVER_ERROR) // 定义错误status
    public UnifyResponse handleException(HttpServletRequest req, Exception e) {
        String requestUrl = req.getRequestURI(); // 请求连接
        String method = req.getMethod(); // 请求方法
        System.out.println(e); // 打印出错误信息
        UnifyResponse message = new UnifyResponse(9999, "服务器异常", method + " "+ requestUrl);
        return message;
    }
    
    @ExceptionHandler(HttpException.class) // 捕获我们定义的http错误类型
    public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest req, HttpException e){
        String requestUrl = req.getRequestURI();
        String method = req.getMethod();
        UnifyResponse message = new UnifyResponse(e.getCode(),codeConfiguration.getMessage(e.getCode()), method + " " + requestUrl);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        // ResponseEntity包含了header信息,返回给前端,对UnifyResponse进行包裹了一层。
        ResponseEntity<UnifyResponse> r = new ResponseEntity<>(message, headers, httpStatus);
        return r;
    }
    
}

那么如何来触发我们的错误呢。很简单,这里定义了一个简单的contrller接口,判断banner是否存在,不存在即可抛出错误。

image.png

最后把目录结构给大家看一看:

image.png

好了,今天的全局异常分享就到这里了,如果有任何问题可以和我一同交流哦。如果对你有用的话,感谢一键3连啦。拜┏(^0^)┛