如何从ServletFilter的ServletResponse中获取HTTP状态码?

我试图报告从我的webapp返回的每个HTTP状态代码。 但是,状态码似乎不能通过ServletResponse访问,或者即使我将其转换为HttpServletResponse。 有没有办法在ServletFilter中获得这个值?

首先,您需要将状态码保存在可访问的地方。 最好用你的实现来包装响应,并保留在那里:

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

为了使用这个包装器,你需要添加一个servletfilter,你可以做你的报告:

 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 } } 

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

所以,如果有升级的空间,升级到Servlet 3.0(Tomcat 7,Glassfish 3,JBoss AS 6等),你不需要包装。

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

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

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

David上面的答案中缺less的一件事是您应该重写sendError的其他forms:

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

编写一个HttpServletResponseWrapper并覆盖所有的setStatus(),sendError()和sendRedirect()方法来logging所有的东西。 编写一个filter,在每个请求中交换你的包装器中的响应对象。

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

 @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; } 

如果您遇到一个较老的容器,那么使用实际状态代码的David Rabinowitz的备用解决scheme(如果它在使用包装器设置后发生更改)是:

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

警告:当使用鬼祟的reflection和内省来获取私有数据值时,很多类层次结构的假设。