你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?

图1  DispatchServlet 继承关系

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第1张图片

图2 一次http请求经过的处理类 tomcat ---coyote---catalina---springframework

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第2张图片

图3 一次http请求经过的处理类 tomcat ---coyote---catalina---springframework

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第3张图片

图4  catalina 中经过的Filter 调用链 ApplicationFilterChain  filter逻辑调用一次忽略处理结果,原路返回 可在catch finally里面做文章

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第4张图片

 

图5 catalina  中Filter  具体实例 继承关系(实现接口)  OrderedHiddenHttpMethodFilter 可以隐藏掉某些http 请求处理方法如 PUT DELETE 等

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第5张图片

图6  springframework 中 DispatcherServlet 中根据request 映射对应的HandlerMapping  

注意:如果请求的URL不存在,会遍历下图所有的HandlerMappings 直到遇到一个能返回默认的HandlerMapping

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第6张图片

图7   springframework 中 DispatcherServlet 的HandlerMapping  实现举例

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第7张图片

图8  如题描述,若果URL不存在,再返回默认的HandlerMapping 图6中的第5个即SimplerUrlHandlerMapping映射的Handler为 ResourceHttpRequestHandler 【 url = /**作为key的handler就是ResourceHttpRequestHandler】,ResourceHttpRequestHandler包含5个拦截器(根据项目不同而不同),如果处理过程中有一个拦截器(本图中第二个JWT拦截器)抛出了异常,则DispatcherServlet中的ha.handler(req,response)不会执行,因为拦截器的方法是有返回值的。

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}

// Actually invoke the handler.  可能没有机会执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第8张图片

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第9张图片

图9 springframework 中 DispatcherServlet 的 HandlerExecutionChain 拦截器处理链

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第10张图片

图10 springframework 中 DispatcherServlet 的 HandlerExecutionChain 拦截器处理链中的拦截器抽象类,后面可以自己定义拦截器并在项目中扩展org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter接口 添加到容器中

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第11张图片

图11 如题描述,若果URL不存在,再返回默认的HandlerMapping 图6中的第5个即SimplerUrlHandlerMapping映射的Handler为 ResourceHttpRequestHandler ,ResourceHttpRequestHandler包含5个拦截器(根据项目不同而不同),如果处理过程中有一个拦截器(本图中第二个JWT拦截器)抛出了异常,后面的处理会经过 org.springframework.web.servlet.DispatcherServlet#processDispatchResult  处理,但是异常无法解析,开始往上层返回,即遍历所有已经经过的拦截器的执行后处理方法(回调)如图8的返回虚线,逆向返回。调用拦截器方法

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第12张图片

图12  异常从DispatcherServlet -->FramworkServlet---->catalina的ApplicationFilterChain ---->ApplicationDispatcher--->异常处理 封装新url=error的请求,再次发送请求。重复上面的down方向的请求

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第13张图片

在次请求error url时对应的handlerMapping变为如下图所示,说明找到了 controller控制器!

根据启动日志也能发现这点!

2019-04-20 23:33:25,152]-[INFO]-[main]-[?:]-[Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2019-04-20 23:33:25,152]-[INFO]-[main]-[?:]-[Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
 

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第14张图片

请求error处理控制器

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第15张图片

springboot默认ERROR处理器✨登场

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第16张图片

最终返回json结果

{"timestamp":1555773618682,"status":500,"error":"Internal Server Error","message":"Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to 120.27.23.54:6379","path":"/api/shandong/huimin/jianglou"}%

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第17张图片

所以我们可以得到情形的分析:

1 拦截器异常&&URL不存在 

   分析:ApplicationDispatcher 会发出二次请求到BasicErrorController 返回500 JSON结果!

    第二次请求会到达BasicErrorController error 方法,而且构建状态码从上次请求的attributes中获取到了 是 500 !

但是!有一种特殊情况 :拦截器异常&&URL不存在 && ApplicationDispatcher 会发出二次请求时拦截器又异常了(即url = error的请求也被拦截器拦截并发生了异常) 会发生什么?

答案是:由org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod来决定,比如业务自己定义了异常处理,则按照业务定义返回JSON数据,可能就不是500了,因为二次请求时拦截器又异常,这个异常完全可以是业务自定义异常,然后转换为自己业务想要的返回结果。即和可能不是500!

比如对 public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 业务方也有相应的异常捕获处理器

如下:没有解析成功

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第18张图片

但是 如果我们业务使用advice注解方式,就能解析自己定义异常了

@ControllerAdvice
public class DefaultExceptionHandler {

    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public BusinessException handlerError(HttpServletRequest req, BusinessException ex) {
        logger.warn("path:{}, queryParam:{},errorCode:{} message:{}", req.getRequestURI(), req.getQueryString(),
                ex.getErrorCode(), ex);
        return ex;
    }

}

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第19张图片

如上就返回的自定义的数据(比如把自定义的异常类返回给前端(返回前端自定义的协议数据),前端自己决定做什么处理)。

至于为什么 有第二次请求,可以参考相关的历史文献,记得分享给我们!

2 拦截器正常&&URL不存在 

  分析:如上图8 还是映射到资源处理器设置状态码404,但是还是同样由ApplicationDispatcher 会发出二次请求最终返回 404!

org.springframework.web.servlet.resource.ResourceHttpRequestHandler

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第20张图片

虽然返回了404 但是committed为false

你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第21张图片

第二次请求同样会到达BasicErrorController error 方法,只不过构建状态码从上次请求的attributes中获取到了 是 404 !你可能猜不到!springboot http请求拦截器Intercepter处理发生异常时会发生什么?500? 404?_第22张图片

3 拦截器异常&&URL存在

   分析:

        第一种情况:同1的"但是!有一种特殊情况 :拦截器异常&&URL不存在 && ApplicationDispatcher 会发出二次请求时拦截器又异常了(即url = error的请求也被拦截器拦截并发生了异常) 会发生什么?"  相当于直接进行 拦截器异常&&URL存在,因为URL对应的controller是存在的,所以可以自定义异常处理器,返回前端自定义的协议数据。

      第二种情况:如果没有异常处理器 

返回:也是500 但不是json化的,默认就是html如下

HTTP Status 500 – Internal Server Error

HTTP Status 500 – Internal Server Error

 

HTTP Status 500 – Internal Server Error

HTTP Status 500 – Internal Server Error

     

      

4 拦截器正常&&URL存在

   分析:正常的业务数据。

 

5  问题 : 如何在小程序应用上避免 500 这么丑的结果呢?  

    1  拦截器不要抛出异常,可以try catch住,让业务逻辑走通即可;

    2  扩展 org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice  检测返回对象,必要时替换掉

    3  拦截器中检测,response 状态码,重写异常数据

你可能感兴趣的:(tomcat,Container,java,springboot,小程序)