How can I get the HTTP status code out of a ServletResponse in a ServletFilter?

JavaServletsServlet FiltersHttp Status-Codes

Java Problem Overview


I'm trying to report on every HTTP status code returned from my webapp. However the status code does not appear to be accessible via the ServletResponse, or even if I cast it to a HttpServletResponse. Is there a way to get access to this value within a ServletFilter?

Java Solutions


Solution 1 - Java

First, you need to save the status code in an accessible place. The best to wrap the response with your implementation and keep it there:

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

}

In order to use this wrapper, you need to add a servlet filter, were you can do your reporting:

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
	}

}

Solution 2 - Java

Since Servlet 3.0, there's a HttpServletResponse#getStatus().

So, if there's room for upgrading, upgrade to Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc) and you don't need a wrapper.

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

Solution 3 - Java

Also need to include a wrapper for #sendRedirect, and it would be better to initialize status to '200' rather than '0'

private int httpStatus = SC_OK;

...

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

Solution 4 - Java

One thing missing from David's answer above is that you should also override the other form of sendError:

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

Solution 5 - Java

In addition to David's answer, you'll also want to override the reset method:

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

... as well as the deprecated setStatus(int, String)

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

Solution 6 - Java

Write an HttpServletResponseWrapper and override all the setStatus(), sendError(), and sendRedirect() methods to log everything. Write a Filter that swaps your wrapper in for the response object on every request.

Solution 7 - Java

If you are stuck with an older container then a alternate solution to David Rabinowitz that uses the actual status code (in case it changes after it is set using the wrapper) is:

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

}

Warning: lots of assumptions of the class hierarchy when using sneaky reflection and introspection to get to private data values.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSeth WeinerView Question on Stackoverflow
Solution 1 - JavaDavid RabinowitzView Answer on Stackoverflow
Solution 2 - JavaBalusCView Answer on Stackoverflow
Solution 3 - JavaJoel HockeyView Answer on Stackoverflow
Solution 4 - JavaWilliam RoseView Answer on Stackoverflow
Solution 5 - JavaGrégory JosephView Answer on Stackoverflow
Solution 6 - JavaLicky LindsayView Answer on Stackoverflow
Solution 7 - JavaJohn JohnsonView Answer on Stackoverflow