2009-08-19 148 views

回答

80

首先,您需要将状态码保存在可访问的位置。最好的包装您的实现的响应,并保持它有:

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 
    } 

} 
+8

的情况下,直到页面结尾才有人阅读,请注意Joel的下面的注释,以便设置默认状态= 200,并覆盖sendRedirect(..) – 2012-10-19 15:56:54

+1

这对于Servlet规范2.4中的较旧版本的Tomcat非常有用。谢谢! – user3621633 2016-03-01 20:13:46

+0

response.sendRedirect()正在给予非法的StateExcpetion。我已经重写了sendRedirect也作为Joel的注释 – 2016-04-11 08:47:08

6

编写HttpServletResponseWrapper并覆盖所有的setStatus(),sendError()和sendRedirect()方法来记录所有内容。编写一个过滤器,用于在每个请求中为您的包装器交换响应对象。

11

有一两件事从上面大卫的回答缺少的是你也应该重写sendError的其它形式:

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

感谢William,我添加了它对我的样品。 – 2010-04-29 06:30:02

59

由于Servlet的3.0,有一个HttpServletResponse#getStatus()。因此,如果有升级空间,请升级到Servlet 3.0(Tomcat 7,Glassfish 3,JBoss AS 6等),并且不需要封装。

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

很高兴有。谢谢先生! – asgs 2014-08-18 06:47:24

+0

那么tomcat 6呢? servlet版本低于3 – 2016-03-30 09:51:28

+0

@Sam:这不是问题的唯一答案。目前接受的答案是如此之久,它仍然适用于Tomcat 6. – BalusC 2016-03-30 09:52:09

14

还需要包括用于#sendRedirect的包装,它会更好,如果你坚持使用旧的容器,然后初始化状态为“200”,而不是“0”

private int httpStatus = SC_OK; 

... 

@Override 
public void sendRedirect(String location) throws IOException { 
    httpStatus = SC_MOVED_TEMPORARILY; 
    super.sendRedirect(location); 
} 
+0

我可以看到您的过滤器映射位置可能会影响您的覆盖代码是否被触发的情况。例如,一个连续的过滤器可能不会包装你的回应,而是取而代之。除了这些情况之外,状态代码是否可以在响应中设置,而不用调用setStatus,sendError或sendRedirect变体?这就是为什么你已经将状态初始化为200? – 1in9ui5t 2012-12-04 16:50:52

0

给David拉比诺维茨一个替代的解决方案,它使用的实际状态代码(在它正在使用的包装器设置后它的变化的情况下)是:

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(); 
    } 

} 

警告:大量的类层次结构的假设的使用SNE时aky反思和内省去获得私人数据价值。

6

除了大卫的回答,您还需要重写复位方法:

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

...以及已过时setStatus(INT,字符串)

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