SpringBoot异常统一处理机制

springboot官网说了三种异常处理,分为机器客户端和浏览器客户端,区别为机器客户端请求返回的是头中Accept是"/"或者空,异常返回值json,浏览器客户端请求头中Accept是"text/html" 返回的是html

如下图:

机器客户端:

浏览器客户端:

下面对两种客户端的三种异常处理方式做简单分析:

一、浏览器客户端:

官网提供两种方式,一种是直接在public.error文件夹中定义静态文件
src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html

第二种是利用模板引擎实现,官网的例子是freemaker(对模板引擎暂不熟悉,未深入了解)

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftl

工程实现:只需生成html即可

二、机器客户端

1、实现方式 继承BasicErrorControllor  

public class MyErrorController extends BasicErrorController {

    public MyErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorProperties, errorViewResolvers);
    }
    @Override
    protected Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
        Map<String, Object> attributes = super.getErrorAttributes(request, includeStackTrace);
        /**
        此处是将默认的返回值删除,并加上项目中定义的异常
         */
        attributes.remove("timestamp");
        attributes.remove("error");
        attributes.remove("path");
        attributes.remove("status");

        String status = attributes.get("message").toString();
        ErrorEnum errorEnum = ErrorEnum.getByCode(status);
        attributes.put("code",errorEnum.getCode());
        attributes.put("message",errorEnum.getMessage());
        attributes.put("canRetry",errorEnum.getCanRetry());
        return attributes;
    }
}

springboot中为了让自定义controller可以使用,需要注册为bean

添加一个配置文件,new MyErrorController的参数可以参考ErrorMvcAutoConfiguration来进行配置

@Configuration

public class ErrorConfig {
    @Bean
    public MyErrorController basicErrorController(ErrorAttributes errorAttributes,ServerProperties serverProperties, DispatcherServletPath dispatcherServletPath, ObjectProvider<List<ErrorViewResolver>> errorViewResolversProvider) {
        return new MyErrorController(errorAttributes, serverProperties.getError(), errorViewResolversProvider.getIfAvailable());
    }

}

加一个异常枚举类

public enum ErrorEnum {
    ID_NOT_NULL("F001", "编号不能为空", false),
    UNKNOWN("999", "不可知异常", false);
    private String code;
    private String message;
    private Boolean canRetry;
    ErrorEnum(String code, String message, Boolean canRetry) {
        this.code = code;
        this.message = message;
        this.canRetry = canRetry;
    }
    public static ErrorEnum getByCode(String code) {
        for (ErrorEnum errorEnum: ErrorEnum.values()) {
            if (errorEnum.code.equals(code)) {
                return errorEnum;
            }
        }
        return UNKNOWN;
    }
    public String getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
    public Boolean getCanRetry() {
        return canRetry;
    }
}

2、使用@ControllerAdvice

此处可以自定义异常,也可以捕获所有的异常,根据需要设置

a、捕获所有的异常

@ControllerAdvice(basePackages = {"com.gcl.manager.Controller"}) //此处是定义的处理异常的包,也可以指定到具体的类
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
    @ExceptionHandler(Exception.class) //此处是处理所有的异常
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        //HttpStatus status = getStatus(request);此处可以不要,需要的话直接copy官网的修改
        /**
         * 此处可以自定义返回类型
         */
        Map<String, Object> attributes = new HashMap<>();
        ErrorEnum errorEnum = ErrorEnum.getByCode(status.value() + ""); //此处非必须这样操作,随意修改
        attributes.put("code",errorEnum.getCode());
        attributes.put("message",errorEnum.getMessage());
        attributes.put("canRetry",errorEnum.getCanRetry());
// new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);

//此处可以定义一个类来处理返回的对象,ResponseEntity接收的是T,HttpStatus
        return new ResponseEntity<>(attributes, HttpStatus.INTERNAL_SERVER_ERROR);
    }

b、捕获对应的异常

与a相比,只需要自定义异常,并catch抛出异常即可

简单异常类

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

任意可能出现异常的地方抛出异常

  try {
            //根据实际校验
            checkProduct(product);
        }catch (Exception e){
            throw new MyCustomerException("校验出错");
        }

最终将a中的Exception.class改为自定义的MyCustomerException.class就可以实现拦截自定义异常,@ExceptionHandler参数是数组,可以捕获多个异常

猜你喜欢

转载自blog.csdn.net/fighterGuy/article/details/82983663