Loading...
墨滴

lyq

2021/11/05  阅读:25  主题:默认主题

Spring Boot默认错误处理

Spring Boot默认提供一个/error映射用来以合适的方式处理所有的错误,并将它注册为servlet容器中全局的 错误页面。对于机器客户端(相对于浏览器而言,浏览器偏重于人的行为),它会产生一个具有详细错误,HTTP状态,异常信息的JSON响应。对于浏览器客户端,它会产生一个白色标签样式(whitelabel)的错误视图,该视图将以HTML格式显示同样的数据(可以添加一个解析为'error'的View来自定义它)。为了完全替换默认的行为,你可以实现ErrorController**,并注册一个该类型的bean定义,或简单地添加一个ErrorAttributes类型的bean以使用现存的机制,只是替换显示的内容。

注: BasicErrorController可以作为自定义ErrorController的基类,如果你想添加对新context type的处理(默认处理text/html),这会很有帮助。你只需要继承BasicErrorController,添加一个public方法,并注解带有produces属性的@RequestMapping,然后创建该新类型的bean

你也可以定义一个@ControllerAdvice去自定义某个特殊controller或exception类型的JSON文档:

@ControllerAdvice(basePackageClasses = FooController.class)
public class FooControllerAdvice extends ResponseEntityExceptionHandler {
 
    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }
 
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
 
}

在以上示例中,如果跟FooController相同package的某个controller抛出YourException,一个CustomerErrorType类型的POJO的json展示将代替ErrorAttributes展示。

自定义错误页面

如果想为某个给定的状态码展示一个自定义的HTML错误页面,你需要将文件添加到/error文件夹下。错误页面既可以是静态HTML(比如,任何静态资源文件夹下添加的),也可以是使用模板构建的,文件名必须是明确的状态码或一系列标签。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

使用FreeMarker模板映射所有5xx错误,你需要如下的目录结构:

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

对于更复杂的映射,你可以添加实现ErrorViewResolver接口的beans:

public class MyErrorViewResolver implements ErrorViewResolver {
 
    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request,
            HttpStatus status, Map<String, Object> model) {
        // Use the request or status to optionally return a ModelAndView
        return ...
    }
 
}

你也可以使用Spring MVC特性,比如@ExceptionHandler方法和@ControllerAdvice,ErrorController将处理所有未处理的异常。

映射Spring MVC以外的错误页面

对于不使用Spring MVC的应用,你可以通过ErrorPageRegistrar接口直接注册ErrorPages。该抽象直接工作于底层内嵌servlet容器,即使你没有Spring MVC的DispatcherServlet,它们仍旧可以工作。

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
    return new MyErrorPageRegistrar();
}
 
// ...
 
private static class MyErrorPageRegistrar implements ErrorPageRegistrar {
 
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }
 
}

注.如果你注册一个ErrorPage,该页面需要被一个Filter处理(在一些非Spring web框架中很常见,比如Jersey,Wicket),那么该Filter需要明确注册为一个ERROR分发器(dispatcher),例如:

@Bean
public FilterRegistrationBean myFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    ...
    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
    return registration;
}

(默认的FilterRegistrationBean不包含ERROR dispatcher类型)。

WebSphere应用服务器的错误处理

当部署到一个servlet容器时,Spring Boot通过它的错误页面过滤器将带有错误状态的请求转发到恰当的错误页面。request只有在response还没提交时才能转发(forwarded)到正确的错误页面,而WebSphere应用服务器8.0及后续版本默认情况会在servlet方法成功执行后提交response,你需要设置com.ibm.ws.webcontainer.invokeFlushAfterService属性为false来关闭该行为。

Spring Boot默认异常处理BasicErrorController源码解读

那么这个页面是怎么返回的呢,这里就要接触到一个Spring Boot类BasicErrorController,如下图

注释信息也说明了,这是一个基础全局错误controller,Spring Boot自带的,看到这个RequestMapping地址,这是一个相当于三元写法,如果你在配置文件配置了server.error.path的话,就会使用你配置的异常处理地址,如果没有就会使用你配置的error.path路径地址,如果还是没有,默认使用/error来作为发生异常的处理地址,如下图

那么这个类是如何处理异常的呢,如下图

它提供了两个接口处理方法,上面一个标注了produces为text/html,当你是网页请求的时候返回的网页数据,下面的接口是当你的请求为其他的时候,返回的是ResponseEntity对象(json数据或者其他,取决与你的返回数据类型配置)。

我们看到第一个接口返回了一个error页面,如果你的项目静态页面下刚好存在一个error所对应的页面,那么Spring Boot会得到你本地的页面,如下图

那么这个BasicErrorController默认返回的信息是哪一些呢,如下图

这个方法是属于类DefaultErrorAttributes,返回一个map,这个map里面包含了异常发生时间,异常状态,异常详细信息,发生异常的请求路径。如下图是一个异常发生的时候所获取到的异常信息

那么,我们该如何抛弃掉或者覆盖掉Spring Boot默认异常处理呢,Spring Boot开发指南上提供了以下四种方法:

  • 自定义一个bean,实现ErrorController接口,那么默认的错误处理机制将不再生效。
  • 自定义一个bean,继承BasicErrorController类,使用一部分现成的功能,自己也可以添加新的public方法,使用**@RequestMapping及其produces**属性指定新的地址映射。
  • 自定义一个ErrorAttribute类型的bean,那么还是默认的两种响应方式,只不过改变了内容项而已。
  • 继承AbstractErrorController

统一处理Exception异常,只需要在类上标注ControllerAdvice这个注解,然后在类方法上标注好对应的ExceptionHandler及异常类。如下图

更多原创文章,请扫码关注我的微信公众号
更多原创文章,请扫码关注我的微信公众号

lyq

2021/11/05  阅读:25  主题:默认主题

作者介绍

lyq