当前位置: 移动技术网 > IT编程>开发语言>Java > java.lang.IllegalStateException: Cannot call sendError() after the response has been committed解读

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

2019年02月21日  | 移动技术网IT编程  | 我要评论

源代码:

@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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网