欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-04-28 12:45:15
源代码: 注:此处对“/”路径的访问返回404. DispatcherServlet.doDispatch()中对拦截 器的preHandle进行调用: mappedHandler是一个HandlerExcutionChain对象由HandlerMapping返回,HandlerExcutionCh ......

源代码:

@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