• java.lang.IllegalStateException: Cannot call sendError() after the response has been committed解读


    源代码:

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        String uri = request.getRequestURI();
        if(pathMatcher.match("/", uri)) {
            System.err.println("跳转");
            response.sendRedirect("/swagger-ui.html");
            // return false;    // 如果此处不返回false, 则springMvc会继续对“/”路径进行处理,就会出现多次返回响应的错误。
            
        }
        return true;
    }
    

    注:此处对“/”路径的访问返回404.

    DispatcherServlet.doDispatch()中对拦截 器的preHandle进行调用:

    // 如果拦截器的PreHandle返回false,则此处直接返回退出方法。
    if(!mappedHandler.applyPreHandle(request,response)){
        return ;
    }
    // Actually invoke the handler. 调用处理器
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    // 由此处可知如果拦截器的preHandle方法返回false则不会调用处理器(控制器类的方法)
    

    mappedHandler是一个HandlerExcutionChain对象由HandlerMapping返回,HandlerExcutionChain包含一个Handler(处理器对象)和拦截器数组,通过applyPreHandle(request,response)方法会对拦截器数组中的每一个拦截器的preHandle进行调用。

    // HandlerExcutionChain类
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
    

    在本例中出现上述错误的原因:

    1. 拦截器拦截了对“/”路径的请求,并且调用response.sendRedirect("/swagger-ui.html")返回了响应。由于拦截器没有返回false,所以SpringMVC会继续对“/”路径进行处理。

    2. 在没有找到“/”对应的处理器时,SpringMVC默认会使用ResourceHttpRequestHandler进行请求处理。ResourceHttpRequestHandler在进行请求处理时会进行404检查,如果路径或资源不存在则会调用response.sendError(HttpServletResponse.SC_NOT_FOUND);源码如下:

      // ResourceHttpRequestHandler中进行404检查
      // For very general mappings (e.g. "/") we need to check 404 first
      Resource resource = getResource(request);
      if (resource == null) {
          logger.debug("Resource not found");
          response.sendError(HttpServletResponse.SC_NOT_FOUND);
          return;
      }
      
    3. 由2可知,如果资源不存在就会调用response.sendError(HttpServletResponse.SC_NOT_FOUND);而在拦截器中已经调用response.sendRedirect("/swagger-ui.html")对响应进行了返回,所以就会出现多次返回响应的错误。

    对于上述问题的解决办法是在response.sendRedirect("/swagger-ui.html");后返回false,或者将拦截路径由“/”改为response.sendError(HttpServletResponse.SC_NOT_FOUND);后的路径(在此处为“/error”)。

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        String uri = request.getRequestURI();
        if(pathMatcher.match("/", uri)) {
    
            response.sendRedirect("/swagger-ui.html");
            return false;    // 如果此处不返回false, 则springMvc会继续对“/”路径进行处理,就会出现多次返回响应的错误。
            
        }
        return true;
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        String uri = request.getRequestURI();
        if(pathMatcher.match("/error", uri)) {
    
            response.sendRedirect("/swagger-ui.html");
            
        }
        return true;
    }
    

    下面是SpringMVC处理"/api/test/error"请求时打印的部分日志(/api/test/error没有对应的处理器,即该路径不存在,报404错误):

    注:自定义的拦截器是在映射结束后才执行的。

    // 请求路径
    2019-02-20 14:55:57.086 DEBUG 2676 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : GET "/api/test/error", parameters={}
    // 处理器映射器和请求路径对应的处理器
    2019-02-20 14:55:57.092 DEBUG 2676 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped to ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]
    // 处理结果
    2019-02-20 14:55:57.094 DEBUG 2676 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler     : Resource not found
    2019-02-20 14:55:57.095 DEBUG 2676 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 404 NOT_FOUND
    

    下面是SpringMVC处理“/api/test/error1”请求时打印的部分日志(/api/test/error1对应的处理器在org.lwt.controller.RoleController类下的.ErrorTest()方法):

    // 请求路径
    2019-02-20 15:13:23.860 DEBUG 2676 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : GET "/api/test/error1", parameters={}
    // 处理器映射器和请求路径对应的处理器
    2019-02-20 15:13:23.861 DEBUG 2676 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.lwt.vo.Result<java.lang.String> org.lwt.controller.RoleController.ErrorTest()
     
    拦截器调用
    // 处理结果
    2019-02-20 15:13:23.865 DEBUG 2676 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : Failed to complete request: org.joda.time.IllegalInstantException: 自己抛出错误
    
    2019-02-20 15:13:23.867 ERROR 2676 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.joda.time.IllegalInstantException: 自己抛出错误] with root cause
    
    
    // RoleController类
    @RestController
    @RequestMapping("/api")
    @Api
    public class RoleController {
        // /api/test/error1对应的处理器
        @GetMapping("/test/error1")
        public Result<String> ErrorTest(){
            throw new IllegalInstantException("自己抛出错误");
            // return Result.success("多参数传递");
        }
    }
    

    下面是SpringMVC处理“/api/test/error1”请求时打印的部分日志(/api/test/error1对应的处理器在org.lwt.controller.RoleController类下的.ErrorTest()方法):

    此处和上一次调用的区别是此次调用处理器没有报错:

    // 请求路径
    2019-02-20 15:21:31.440 DEBUG 8252 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : GET "/api/test/error1", parameters={}
    // 处理器映射器和请求路径对应的处理器
    2019-02-20 15:21:31.444 DEBUG 8252 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public org.lwt.vo.Result<java.lang.String> org.lwt.controller.RoleController.ErrorTest()
        
    拦截器中的uri: /api/test/error1
    
    // 处理结果
    2019-02-20 15:21:31.473 DEBUG 8252 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json;q=0.8', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [application/json, application/*+json, application/json, application/*+json]
    
    2019-02-20 15:21:31.473 DEBUG 8252 --- [nio-8080-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Writing [org.lwt.vo.Result@334348d5]
    
    2019-02-20 15:21:31.486 DEBUG 8252 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 200 OK
    
  • 相关阅读:
    html <applet>元素属性介绍
    C#内存释放(转)
    mongodb for linux (安装)
    WCF客户端搭建(通过自定义WCF Client封装) wu
    SQL常用语句 wu
    任务调度平台 wu
    将DataSet 纵向显示数据
    关于OP和SI项目的记录点,防止遗忘难以查找
    java进阶
    git代码量统计
  • 原文地址:https://www.cnblogs.com/yourblog/p/10407056.html
Copyright © 2020-2023  润新知