Java 如何从ServletFilter中的ServletResponse中获取HTTP状态代码?
我正在尝试报告从我的webapp返回的每个HTTP状态代码。但是,状态代码似乎无法通过ServletResponse访问,或者即使我将其转换为HttpServletResponse。是否有办法在ServletFilter中访问此值?编写一个HttpServletResponseWrapper并重写所有setStatus()、sendError()和sendRedirect()方法以记录所有内容。编写一个过滤器,在每次请求时为响应对象交换包装器。首先,您需要将状态代码保存在一个可访问的位置。最好将响应与您的实现打包并保存在一起:Java 如何从ServletFilter中的ServletResponse中获取HTTP状态代码?,java,servlets,servlet-filters,http-status-codes,Java,Servlets,Servlet Filters,Http Status Codes,我正在尝试报告从我的webapp返回的每个HTTP状态代码。但是,状态代码似乎无法通过ServletResponse访问,或者即使我将其转换为HttpServletResponse。是否有办法在ServletFilter中访问此值?编写一个HttpServletResponseWrapper并重写所有setStatus()、sendError()和sendRedirect()方法以记录所有内容。编写一个过滤器,在每次请求时为响应对象交换包装器。首先,您需要将状态代码保存在一个可访问的位置。最好将
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;
}
}
为了使用此包装器,您需要添加一个servlet过滤器,前提是您可以进行报告:
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
}
}
上面David的回答中缺少的一点是,您还应该覆盖另一种形式的sendError:
@Override
public void sendError(int sc, String msg) throws IOException {
httpStatus = sc;
super.sendError(sc, msg);
}
自Servlet3.0以来,有一个 因此,如果有升级的空间,可以升级到Servlet3.0(Tomcat7、GlassFish3、JBossAS6等),而不需要包装器
chain.doFilter(request, response);
int status = ((HttpServletResponse) response).getStatus();
还需要包含#sendRedirect的包装,最好将状态初始化为“200”而不是“0”
private int httpStatus = SC_OK;
...
@Override
public void sendRedirect(String location) throws IOException {
httpStatus = SC_MOVED_TEMPORARILY;
super.sendRedirect(location);
}
如果您使用的是较旧的容器,那么David Rabinowitz的另一个解决方案是使用实际状态代码(如果在使用包装器设置后发生更改):
公共类StatusExposingServletResponse扩展了HttpServletResponseWrapper{
公共状态ExposingServletResponse(HttpServletResponse){
超级(响应);
}
@凌驾
公共void发送错误(int sc)引发IOException{
超级发送错误(sc);
}
@凌驾
public void sendError(int sc,String msg)引发IOException{
超级发送错误(sc,msg);
}
@凌驾
公共状态(内部sc){
super.setStatus(sc);
}
public int getStatus(){
试一试{
ServletResponse对象=super.getResponse();
//调用私有方法“getResponse”
方法method1=object.getClass().getMethod(“getResponse”);
Object servletResponse=method1.invoke(对象,新对象[]{});
//调用父级私有方法“getResponse”
方法method2=servletResponse.getClass().getMethod(“getResponse”);
objectparentresponse=method2.invoke(servletResponse,新对象[]{});
//调用父级私有方法“getResponse”
方法method3=parentResponse.getClass().getMethod(“getStatus”);
int-httpStatus=(整数)method3.invoke(parentResponse,新对象[]{});
返回httpStatus;
}
捕获(例外e){
e、 printStackTrace();
返回HttpServletResponse.SC\u已接受;
}
}
公共字符串getMessage(){
试一试{
ServletResponse对象=super.getResponse();
//调用私有方法“getResponse”
方法method1=object.getClass().getMethod(“getResponse”);
Object servletResponse=method1.invoke(对象,新对象[]{});
//调用父级私有方法“getResponse”
方法method2=servletResponse.getClass().getMethod(“getResponse”);
objectparentresponse=method2.invoke(servletResponse,新对象[]{});
//调用父级私有方法“getResponse”
方法method3=parentResponse.getClass().getMethod(“getReason”);
字符串httpStatusMessage=(字符串)method3.invoke(parentResponse,新对象[]{});
如果(httpStatusMessage==null){
int status=getStatus();
java.lang.reflect.Field[]fields=HttpServletResponse.class.getFields();
for(java.lang.reflect.Field:fields){
if(status==field.getInt(servletResponse)){
httpStatusMessage=field.getName();
httpStatusMessage=httpStatusMessage.replace(“SC_”)和;
如果(!“OK.”等于(httpStatusMessage)){
httpStatusMessage=httpStatusMessage.toLowerCase();
httpStatusMessage=httpStatusMessage.replace(““,”);
httpStatusMessage=大写字母(httpStatusMessage);
}
打破
}
}
}
返回httpStatusMessage;
}
捕获(例外e){
e、 printStackTrace();
返回“”;
}
}
专用静态字符串大写字母(字符串s){
对于(int i=0;i
警告:当使用秘密反射和内省来获取私有数据值时,会对类层次结构进行大量假设。除了David的答案之外,您还需要覆盖重置方法:
@Override
public void reset() {
super.reset();
this.httpStatus = SC_OK;
}
。。。以及不推荐使用的setStatus(int,String)
谢谢William,我已经将它添加到了我的示例中。如果有人在页面结束前没有阅读,请注意Joel下面的评论,将默认状态设置为200,并覆盖sendRedirect(..),这对Servlet上的旧版本Tomcat非常有帮助
@Override
public void reset() {
super.reset();
this.httpStatus = SC_OK;
}
@Override
public void setStatus(int status, String string) {
super.setStatus(status, string);
this.httpStatus = status;
}