web-dev-qa-db-fra.com

Comment puis-je extraire le code d'état HTTP d'un ServletResponse dans un ServletFilter?

J'essaie de faire rapport sur chaque code d'état HTTP renvoyé par mon application Web. Cependant, le code d'état ne semble pas être accessible via ServletResponse, ni même si je le convertis en HttpServletResponse. Existe-t-il un moyen d'obtenir l'accès à cette valeur dans un ServletFilter?

58
Seth Weiner

Tout d’abord, vous devez enregistrer le code de statut dans un endroit accessible. Le mieux pour encapsuler la réponse avec votre implémentation et la garder là:

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

}

Pour utiliser ce wrapper, vous devez ajouter un filtre de servlet, où vous pouvez faire votre 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
    }

}
83
David Rabinowitz

Depuis Servlet 3.0, il y a une HttpServletResponse#getStatus() .

Donc, s'il y a de la place pour la mise à niveau, passez à Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, etc.) et vous n'avez pas besoin d'un wrapper.

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

Il faut également inclure un wrapper pour #sendRedirect, et il serait préférable d’initialiser le statut à «200» plutôt qu’à «0».

private int httpStatus = SC_OK;

...

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

Une des choses qui manque à la réponse de David ci-dessus, c'est que vous devez également remplacer l'autre forme de sendError:

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

En plus de la réponse de David, vous voudrez également remplacer la méthode de réinitialisation:

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

... ainsi que la méthode obsolète setStatus (int, String)

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

Ecrivez un HttpServletResponseWrapper et remplacez toutes les méthodes setStatus (), sendError () et sendRedirect () pour tout consigner. Ecrivez un filtre qui permette à votre enveloppe de recevoir l'objet de réponse à chaque demande.

6
Licky Lindsay

Si vous êtes coincé avec un conteneur plus ancien, une solution alternative à David Rabinowitz utilisant le code de statut réel (au cas où il changerait après sa définition à l'aide du wrapper) est la suivante:

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

}

Avertissement: beaucoup d’hypothèses de la hiérarchie des classes lorsqu’on utilise la réflexion sournoise et l’introspection pour obtenir des valeurs de données privées.

0
John Johnson