2009-08-19 37 views
56

Tôi đang cố gắng báo cáo về mọi mã trạng thái HTTP được trả về từ webapp của tôi. Tuy nhiên, mã trạng thái dường như không thể truy cập được thông qua ServletResponse, hoặc thậm chí nếu tôi đưa nó vào một HttpServletResponse. Có cách nào để truy cập vào giá trị này trong ServletFilter không?Làm cách nào để lấy mã trạng thái HTTP ra khỏi ServletResponse trong ServletFilter?

Trả lời

80

Trước tiên, bạn cần phải lưu mã trạng thái vào một nơi có thể truy cập. Các tốt nhất để quấn phản ứng với thực hiện của bạn và giữ nó ở đó:

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

} 

Để sử dụng wrapper này, bạn cần phải thêm một bộ lọc servlet, là bạn có thể làm báo cáo của bạn:

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

trong trường hợp ai đó không đọc cho đến cuối trang, hãy xem bình luận của Joel bên dưới để đặt trạng thái mặc định = 200 và cũng ghi đè lên sendRedirect (..) –

+1

Điều này cực kỳ hữu ích cho phiên bản cũ của Tomcat có trong Servlet spec 2.4. Cảm ơn bạn! – user3621633

+0

response.sendRedirect() đang cho phép bất hợp phápStateExcpetion. Tôi đã overridded sendRedirect cũng như bình luận của Joel –

6

Viết một HttpServletResponseWrapper và ghi đè tất cả các phương thức setStatus(), sendError() và sendRedirect() để ghi lại mọi thứ. Viết một Bộ lọc hoán đổi trình bao bọc của bạn cho đối tượng phản hồi trên mọi yêu cầu.

11

một điều mất tích từ câu trả lời của David trên là bạn cũng nên ghi đè lên các hình thức khác của sendError:

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

Cảm ơn William, tôi đã thêm nó cho mẫu của tôi. –

59

Kể từ Servlet 3.0, có một HttpServletResponse#getStatus().

Vì vậy, nếu có chỗ để nâng cấp, hãy nâng cấp lên Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, v.v.) và bạn không cần trình bao bọc.

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

thật tuyệt. cảm ơn ngài! – asgs

+0

Còn tomcat 6 thì sao ?? phiên bản servlet dưới 3 –

+0

@Sam: đây không phải là câu trả lời duy nhất cho câu hỏi. Câu trả lời hiện đang được chấp nhận quá cũ nó vẫn áp dụng cho Tomcat 6. – BalusC

14

Cũng cần phải bao gồm một wrapper cho #sendRedirect, và nó sẽ là tốt hơn để khởi tạo trạng thái để '200' chứ không phải '0'

private int httpStatus = SC_OK; 

... 

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

Tôi có thể thấy các tình huống mà vị trí ánh xạ bộ lọc của bạn có thể tác động đến việc mã ghi đè của bạn có được kích hoạt hay không. Ví dụ, một bộ lọc liên tiếp có thể không bao gồm phản hồi của bạn mà thay thế nó. Bên cạnh những tình huống đó, mã trạng thái có thể được đặt trên phản hồi mà không cần gọi các biến setStatus, sendError hoặc sendRedirect không? Đó có phải là lý do tại sao bạn đã khởi tạo trạng thái là 200? – 1in9ui5t

0

Nếu bạn đang mắc kẹt với một container cũ rồi một giải pháp thay thế để David Rabinowitz có sử dụng mã trạng thái thực tế (trong trường hợp nó thay đổi sau khi nó được thiết lập bằng cách sử dụng wrapper) là:

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

} 

Cảnh báo: rất nhiều giả định của hệ thống phân cấp lớp khi sử dụng SNE phản ánh và quan tâm đến các giá trị dữ liệu riêng tư.

6

Ngoài câu trả lời của David, bạn cũng sẽ muốn ghi đè lên các phương pháp thiết lập lại:

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

... cũng như phản đối setStatus (int, String)

@Override 
public void setStatus(int status, String string) { 
    super.setStatus(status, string); 
    this.httpStatus = status; 
} 
Các vấn đề liên quan