【问题标题】:How can I get the HTTP status code out of a ServletResponse in a ServletFilter?如何从 ServletFilter 中的 ServletResponse 中获取 HTTP 状态代码?
【发布时间】:2010-11-21 02:02:55
【问题描述】:

我正在尝试报告从我的 web 应用返回的每个 HTTP 状态代码。但是,状态代码似乎无法通过 ServletResponse 访问,或者即使我将其转换为 HttpServletResponse。有没有办法在 ServletFilter 中访问这个值?

【问题讨论】:

    标签: java servlets servlet-filters http-status-codes


    【解决方案1】:

    首先,您需要将状态代码保存在可访问的位置。最好将响应与您的实现一起包装并保留:

    public class StatusExposingServletResponse extends HttpServletResponseWrapper {
    
        private int httpStatus;
    
        public StatusExposingServletResponse(HttpServletResponse response) {
            super(response);
        }
    
        @Override
        public void sendError(int sc) throws IOException {
            httpStatus = sc;
            super.sendError(sc);
        }
    
        @Override
        public void sendError(int sc, String msg) throws IOException {
            httpStatus = sc;
            super.sendError(sc, msg);
        }
    
    
        @Override
        public void setStatus(int sc) {
            httpStatus = sc;
            super.setStatus(sc);
        }
    
        public int getStatus() {
            return httpStatus;
        }
    
    }
    

    为了使用这个包装器,你需要添加一个 servlet 过滤器,如果你可以做你的报告:

    public class StatusReportingFilter implements Filter {
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            StatusExposingServletResponse response = new StatusExposingServletResponse((HttpServletResponse)res);
            chain.doFilter(req, response);
            int status = response.getStatus();
            // report
        }
    
        public void init(FilterConfig config) throws ServletException {
            //empty
        }
    
        public void destroy() {
            // empty
        }
    
    }
    

    【讨论】:

    • 如果有人直到页面末尾才阅读,请注意下面 Joel 的评论,同时设置默认状态=200 并覆盖 sendRedirect(..)
    • 这对于使用 Servlet 规范 2.4 的旧版本 Tomcat 非常有帮助。谢谢!
    • response.sendRedirect() 给出了非法状态异常。作为乔尔的评论,我也覆盖了 sendRedirect
    • 自 Servlet 3.0 以来有 HttpServletRepsone.getStatus() - 请参阅 BalusC 的回答:stackoverflow.com/a/4305235/280244
    【解决方案2】:

    从 Servlet 3.0 开始,有一个 HttpServletResponse#getStatus()

    因此,如果有升级空间,请升级到 Servlet 3.0(Tomcat 7、Glassfish 3、JBoss AS 6 等),并且不需要包装器。

    chain.doFilter(request, response);
    int status = ((HttpServletResponse) response).getStatus();
    

    【讨论】:

    • tomcat 6 怎么样?? servlet 版本低于 3
    • @Sam:这不是问题的唯一答案。当前接受的答案太旧了,它仍然适用于 Tomcat 6。
    • Tomcat 6 不支持 request.getStatus()... 该怎么做
    • @Sam:这不是问题的唯一答案。当前接受的答案太旧了,它仍然适用于 Tomcat 6。
    • 但是当我这样做时我得到 0。
    【解决方案3】:

    还需要为#sendRedirect 包含一个包装器,最好将状态初始化为'200'而不是'0'

    private int httpStatus = SC_OK;
    
    ...
    
    @Override
    public void sendRedirect(String location) throws IOException {
        httpStatus = SC_MOVED_TEMPORARILY;
        super.sendRedirect(location);
    }
    

    【讨论】:

    • 我可以看到过滤器映射位置会影响覆盖代码是否被触发的情况。例如,一个连续的过滤器可能不会包装您的响应,而是替换它。除了这些场景之外,是否可以在不调用 setStatus、sendError 或 sendRedirect 变体的情况下在响应中设置状态代码?这就是您将状态初始化为 200 的原因吗?
    • @Joel 你能在这里提一下为什么要使用 SC_OK 吗?我遇到了同样的问题,没有调用 responseWrapper setStatus() 并且 httpStatus 仍然是0
    【解决方案4】:

    上面大卫的回答中缺少的一件事是您还应该覆盖另一种形式的 sendError:

    @Override
    public void sendError(int sc, String msg) throws IOException {
        httpStatus = sc;
        super.sendError(sc, msg);
    }
    

    【讨论】:

    • 感谢 William,我已将其添加到我的示例中。
    【解决方案5】:

    除了大卫的回答之外,您还需要覆盖重置方法:

    @Override
    public void reset() {
        super.reset();
        this.httpStatus = SC_OK;
    }
    

    ... 以及已弃用的 setStatus(int, String)

    @Override
    public void setStatus(int status, String string) {
        super.setStatus(status, string);
        this.httpStatus = status;
    }
    

    【讨论】:

      【解决方案6】:

      编写一个 HttpServletResponseWrapper 并覆盖所有 setStatus()、sendError() 和 sendRedirect() 方法以记录所有内容。编写一个过滤器,将您的包装器换成每个请求的响应对象。

      【讨论】:

        【解决方案7】:

        如果您被旧容器卡住,那么使用实际状态代码的 David Rabinowitz 的替代解决方案是:

        public class StatusExposingServletResponse extends HttpServletResponseWrapper {
        
            public StatusExposingServletResponse(HttpServletResponse response) {
                super(response);
            }
        
            @Override
            public void sendError(int sc) throws IOException {
                super.sendError(sc);
            }
        
            @Override
            public void sendError(int sc, String msg) throws IOException {
                super.sendError(sc, msg);
            }
        
            @Override
            public void setStatus(int sc) {
                super.setStatus(sc);
            }
        
            public int getStatus() {
                try {
                    ServletResponse object = super.getResponse();
        
                    // call the private method 'getResponse'
                    Method method1 = object.getClass().getMethod("getResponse");
                    Object servletResponse = method1.invoke(object, new Object[] {});
        
                    // call the parents private method 'getResponse'
                    Method method2 = servletResponse.getClass().getMethod("getResponse");
                    Object parentResponse = method2.invoke(servletResponse, new Object[] {});
        
                    // call the parents private method 'getResponse'
                    Method method3 = parentResponse.getClass().getMethod("getStatus");
                    int httpStatus = (Integer) method3.invoke(parentResponse, new Object[] {});
        
                    return httpStatus;
                }
                catch (Exception e) {
                    e.printStackTrace();
                    return HttpServletResponse.SC_ACCEPTED;
                }
            }
        
            public String getMessage() {
                try {
                    ServletResponse object = super.getResponse();
        
                    // call the private method 'getResponse'
                    Method method1 = object.getClass().getMethod("getResponse");
                    Object servletResponse = method1.invoke(object, new Object[] {});
        
                    // call the parents private method 'getResponse'
                    Method method2 = servletResponse.getClass().getMethod("getResponse");
                    Object parentResponse = method2.invoke(servletResponse, new Object[] {});
        
                    // call the parents private method 'getResponse'
                    Method method3 = parentResponse.getClass().getMethod("getReason");
                    String httpStatusMessage = (String) method3.invoke(parentResponse, new Object[] {});
        
                    if (httpStatusMessage == null) {
                        int status = getStatus();
                        java.lang.reflect.Field[] fields = HttpServletResponse.class.getFields();
        
                        for (java.lang.reflect.Field field : fields) {
                            if (status == field.getInt(servletResponse)) {
                                httpStatusMessage = field.getName();
                                httpStatusMessage = httpStatusMessage.replace("SC_", "");
                                if (!"OK".equals(httpStatusMessage)) {
                                    httpStatusMessage = httpStatusMessage.toLowerCase();
                                    httpStatusMessage = httpStatusMessage.replace("_", " ");
                                    httpStatusMessage = capitalizeFirstLetters(httpStatusMessage);
                                }
        
                                break;
                            }
                        }
                    }
        
                    return httpStatusMessage;
                }
                catch (Exception e) {
                    e.printStackTrace();
                    return "";
                }
            }
        
            private static String capitalizeFirstLetters(String s) {
        
                for (int i = 0; i < s.length(); i++) {
                    if (i == 0) {
                        // Capitalize the first letter of the string.
                        s = String.format("%s%s", Character.toUpperCase(s.charAt(0)), s.substring(1));
                    }
        
                    if (!Character.isLetterOrDigit(s.charAt(i))) {
                        if (i + 1 < s.length()) {
                            s = String.format("%s%s%s", s.subSequence(0, i + 1), 
                                    Character.toUpperCase(s.charAt(i + 1)), 
                                    s.substring(i + 2));
                        }
                    }
                }
        
                return s;
        
            }
        
            @Override
            public String toString() {
                return this.getMessage() + " " + this.getStatus();
            }
        
        }
        

        警告:在使用偷偷摸摸的反射和自省来获取私有数据值时,对类层次结构有很多假设。

        【讨论】:

          猜你喜欢
          • 2012-08-21
          • 1970-01-01
          • 1970-01-01
          • 2011-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多