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

前端 未结 7 1930
说谎
说谎 2020-11-28 04:16

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

相关标签:
7条回答
  • 2020-11-28 04:23

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 04:24

    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;
    }
    
    0 讨论(0)
  • 2020-11-28 04:32

    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();
    
    0 讨论(0)
  • 2020-11-28 04:33

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 04:35

    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.

    0 讨论(0)
  • 2020-11-28 04:38

    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
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题