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

 其实就是在web项目中添加了一个过滤器,作为平台的开关,实现代码如下:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        if (!httpRequest.getServletPath().equals("/switch/lock")
                && !httpRequest.getServletPath().equals("/switch/unlock")) {
            
            logger.error("ServletPath: " + httpRequest.getServletPath());

            boolean switcherLock = false;
            try {
                switcherLock = SwitchServiceImpl.getInstance().getSwitchLock();
                logger.error("后台当前锁状态是: " + switcherLock);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (switcherLock) {
                httpResponse.sendError(403);
            }
        }            
        chain.doFilter(request, response);

    }

web.xml
        
    <filter>
        <filter-name>switchfilter</filter-name>
        <filter-class>com.jieve.filter.SwitchFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>switchfilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <error-page>
        <error-code>404</error-code>
        <location>/views/common/404.html</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/views/common/500.html</location>
    </error-page>
    <error-page>
        <error-code>403</error-code>
        <location>/views/common/403.html</location>
    </error-page>



说下这个问题出现的原因,报错内容`Cannot call sendError() after the response has been committed`从字面上可以看出是因为response已经提交过了,所以不能调用sendError()方法。


首先说下,过滤器是链式结构的,就是说配了多个过滤器的话,通过`chain.doFilter(request, response);`可以依次链式调用,如果说出现response多次提交的话,应该就是其中一个过滤器response提交之后,再通过调用`chain.doFilter(request, response);`方法将response传递到下一个过滤器;或者在一个过滤器内多次提交response。

排除第二种情况出现的可能性,看下sendError()方法实现:

    public void sendError(int sc) throws IOException {
        this._getHttpServletResponse().sendError(sc);
    }

方法的官方注释如下:

    sendError(int sc):使用指定的状态码并清空缓冲,发送一个错误响应至客户端。如果响应已经被提交,
    这个方法会抛出IllegalStateException。使用这个方法后,响应则应该被认为已被提交,且不应该再被进行写操作了。

可以看出是在`httpResponse.sendError(403);`执行提交了response,所以`chain.doFilter(request, response);`传递到下个过滤器的response不可再被提交,即报错`java.lang.IllegalStateException: Cannot call sendError() after the response has been committed`

解决思路是保证在任何情况下都保证response单次提交,修改代码如下:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        if (!httpRequest.getServletPath().equals("/switch/lock")
                && !httpRequest.getServletPath().equals("/switch/unlock")) {
            
            logger.error("ServletPath: " + httpRequest.getServletPath());

            boolean switcherLock = false;
            try {
                switcherLock = SwitchServiceImpl.getInstance().getSwitchLock();
                logger.error("后台当前锁状态是: " + switcherLock);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (switcherLock) {
                httpResponse.sendError(403);
            }else {
                chain.doFilter(request, response);
            }
        }else {
            
            chain.doFilter(request, response);
        }

    }

细化处理错误的分支,部署,测试,问题解决。
阅读更多

servlet:Cannot call sendError() after the response has been committed

04-12

[code=Java]rn//这是servlet程序rnpackage cn.eden.httpServer;rnrnimport java.io.*;rnimport javax.servlet.http.*;rnimport javax.servlet.*;rnrnimport cn.eden.connetGameWorld.Client;rnrnpublic class HelloWebTest extends HttpServlet rn rn @Overridern protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOExceptionrn rn String username =req.getParameter("username"); rn String password =req.getParameter("password"); rn System.out.println("Thereceived username and password is: " + username + "/" +password);rn rn PrintWriter out =resp.getWriter();rn out.print("OK");rn out.flush();rn out.close();rn super.doPost(req, resp);rn rnrnrn[/code]rnrn[code=Java]rn//这是模拟HTTP请求rnpublic class PostRequestEmulatorrnrnrn public static void main(String[] args) throws Exception rn // 服务地址rn URL url = new URL("http://127.0.0.1:8080/HelloWebTest/test");rn // 设定连接的相关参数rn HttpURLConnection connection = (HttpURLConnection) url.openConnection();rn connection.setDoOutput(true);rn connection.setRequestMethod("POST");rn OutputStreamWriter out = new OutputStreamWriter(rn connection.getOutputStream(), "UTF-8");rn // 向服务端发送key = value对rn out.write("username=kevin&password=pass");rn out.flush();rn out.close();rnrn // 获取服务端的反馈rn String strLine = "";rn String strResponse = "";rn InputStream in = connection.getInputStream();rn BufferedReader reader = new BufferedReader(new InputStreamReader(in));rn while ((strLine = reader.readLine()) != null) rn strResponse += strLine + "\n";rn rn System.out.print(strResponse);rn rnrnrnrn[/code]rnrn启动Tomcat后,启动模拟请求。出现rn[code=Java]rnThereceived username and password is: kevin/passrnrn2012-4-12 15:55:46 org.apache.catalina.core.StandardWrapperValve invokern严重: Servlet.service() for servlet [test] in context with path [/HelloWebTest] threw exceptionrnjava.lang.IllegalStateException: Cannot call sendError() after the response has been committedrnrnrn[/code]rn

没有更多推荐了,返回首页