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; }
在本例中出现上述错误的原因:
拦截器拦截了对“/”路径的请求,并且调用response.sendredirect("/swagger-ui.html")返回了响应。由于拦截器没有返回false,所以springmvc会继续对“/”路径进行处理。
-
在没有找到“/”对应的处理器时,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; }
由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
推荐阅读
-
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed解读
-
java.lang.IllegalStateException: Cannot forward after response has been committed
-
Cannot call reset() after response has been committed
-
Hibernate项目报错:Cannot call sendError() after the response has been committed
-
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed解读
-
Cannot forward after response has been committed问题分析及解决
-
Hibernate项目报错:Cannot call sendError() after the response has been committed
-
Cannot call sendError() after the response has been committed 错误记录
-
Cannot call sendError() after the response has been committed
-
错误提示:Cannot call sendError() after the response has been committed的解决方法