Próbuję zgłosić każdy kod statusu HTTP zwrócony z mojej aplikacji internetowej. Jednak wydaje się, że kod statusu nie jest dostępny za pośrednictwem ServletResponse, a nawet jeśli rzuciłem go na HttpServletResponse. Czy istnieje sposób, aby uzyskać dostęp do tej wartości w ServletFilter?Jak mogę uzyskać kod statusu HTTP z ServletResponse w ServletFilter?
Odpowiedz
Najpierw należy zapisać kod statusu w dostępnym miejscu. Najlepiej owinąć odpowiedź z implementacji i utrzymania go tam:
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;
}
}
W celu korzystania z tej owijki, trzeba dodać filtr serwletu, w którym można wykonać sprawozdawczym:
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
}
}
Napisz HttpServletResponseWrapper i zastąp wszystkie metody setStatus(), sendError() i sendRedirect(), aby rejestrować wszystko. Napisz filtr, który zamienia twoje opakowanie obiektu odpowiedzi na każde żądanie.
Jedną rzeczą, brakuje odpowiedzi Dawida powyższego jest to, że należy również zastąpić inną formę sendError:
@Override
public void sendError(int sc, String msg) throws IOException {
httpStatus = sc;
super.sendError(sc, msg);
}
Dzięki William, dodałem to do mojej próbki. –
od Servlet 3.0, istnieje HttpServletResponse#getStatus()
.
Tak więc, jeśli jest miejsce na uaktualnienie, zaktualizuj do Servlet 3.0 (Tomcat 7, Glassfish 3, JBoss AS 6, itd.) I nie potrzebujesz opakowania.
chain.doFilter(request, response);
int status = ((HttpServletResponse) response).getStatus();
Należy również zawierać otoki dla #sendRedirect, i byłoby lepiej, aby zainicjować status na „200” zamiast „0”
private int httpStatus = SC_OK;
...
@Override
public void sendRedirect(String location) throws IOException {
httpStatus = SC_MOVED_TEMPORARILY;
super.sendRedirect(location);
}
Widzę sytuacje, w których miejsce docelowe mapowania filtra może mieć wpływ na to, czy zostanie włączony wyzwalający kod. Na przykład, kolejny filtr może nie zawijać twojej odpowiedzi, ale raczej ją zastąpić. Poza tymi scenariuszami, czy kod statusu można ustawić na odpowiedź bez wywoływania wariantów setStatus, sendError lub sendRedirect? Czy to dlatego zainicjalizowałeś status na 200? – 1in9ui5t
Jeśli utkniesz ze starszym pojemniku następnie zastępcy rozwiązanie David Rabinowitz, który wykorzystuje kod stanu faktycznego (w przypadku zmiany po to jest ustawiana za pomocą owijki) wynosi:
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: wiele założeń hierarchii klasowej podczas korzystania SNE jako odbicie i introspekcję, aby uzyskać prywatne wartości danych.
Oprócz odpowiedzi Dawida, będziesz także chcą zastąpić metodę Reset:
@Override
public void reset() {
super.reset();
this.httpStatus = SC_OK;
}
... a także przestarzałe setStatus (int, string)
@Override
public void setStatus(int status, String string) {
super.setStatus(status, string);
this.httpStatus = status;
}
w przypadku ktoś nie czyta do końca strony, uważaj na komentarz Joela poniżej, aby ustawić domyślny status = 200, a także nadpisać sendRedirect (..) –
To było bardzo pomocne dla starszej wersji Tomcat, która jest w Servlet spec 2.4. Dziękuję Ci! – user3621633
response.sendRedirect() podaje nielegalneStateExcpetion. Nadpisałem sendRedirect także jako komentarz Joela –