📄 servlet2.3.txt
字号:
在这些类中,这两个方法的默认行为是把调用传递给它们所封装的对象。使用这些类的时候,你一般要派生这些类并覆盖那些感兴趣的方法。对于本文的过滤器,我们必须创建一个应答封装器,记录对addXXXHeader()方法和setXXXHeader()方法的调用。另外,我们还想要捕获对setStatus()、setContentLength()、setContentType()和setLocale()方法的调用。代码如Listing 4所示,它简单地记录了对各个方法的调用。
【Listing 4】应答封装器。下面的代码为过滤器构造了一个应答封装器, 它将记录对addXXXHeader()和setXXXHeader()方法的调用。另外, 它还要捕获对setStatus()、setContentLength()、setContentType()和 setLocale()方法的调用。
class HeaderResponseWrapper extends
HttpServletResponseWrapper {
ServletContext ctx;
public HeaderResponseWrapper(
HttpServletResponse response,
ServletContext ctx) {
super(response);
this.ctx = ctx;
}
public void addCookie(Cookie cookie) {
ctx.log("Set-Cookie: " + cookie.getName() + ":" + cookie.getValue());
super.addCookie(cookie);
}
public void addHeader(String name, String value) {
ctx.log(name + ": " + value); super.addHeader(name, value);
}
public void addIntHeader(String name, int value) {
ctx.log(name + ": " + value); super.addIntHeader(name, value);
}
public void addDateHeader(String name, long value) {
ctx.log(name + ": " + value); super.addDateHeader(name, value);
}
public void setHeader( String name, String value) {
ctx.log(name + ": " + value); super.setHeader(name, value);
}
public void setIntHeader( String name, int value) {
ctx.log(name + ": " + value); super.setIntHeader(name, value);
}
public void setDateHeader( String name, long value) {
ctx.log(name + ": " + value); super.setDateHeader(name, value);
}
public void setStatus(int sc) {
ctx.log("status: " + sc); super.setStatus(sc);
}
public void setStatus( int sc, java.lang.String sm) {
ctx.log("status: " + sc); super.setStatus(sc, sm);
}
public void setContentLength(int len) {
ctx.log("Content-Length: " + len); super.setContentLength(len);
}
public void setContentType(java.lang.String type) {
ctx.log("Content-Type: " + type); super.setContentType(type);
}
public void setLocale(java.util.Locale loc) {
ctx.log("locale: " + loc); super.setLocale(loc);
}
}
在过滤器中,我们按照如下方式使用这个对象:
HttpServletResponse resp = (HttpServletResponse)response;
HeaderResponseWrapper hrespw = new HeaderResponseWrapper(resp, ctx);
System.out.println("********");
chain.doFilter(request, hrespw);
注意创建好封装器之后chain.doFilter()方法就被调用。
定义好过滤器之后就应该安装它。为此,web.xml中应该定义一个filter元素:
<filter>
<filter-name>
Headers Filter
</filter-name>
<filter-class>
DumpHeaders
</filter-class>
<!- optional <init-params> ->
</filter>
完成这一步之后,你还要把过滤器和你想要过滤的资源关联起来。这时你有两种选择:在web.xml中,把过滤器关联到单个命名的Servlet,或者把过滤器关联到一个URL。如下所示:
<filter-mapping>
<filter-name>
Headers Filter
</filter-name>
<servlet-name>
AddressBookServlet
</servlet-name>
</filter-mapping>
或者:
<filter-mapping>
<filter-name>
Header Filter
</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
输出的请求头如下所示:
GET/AddressBook/Browse.jsp HTTP/1.1
accept: */*
referer: http://localhost/ AddressBook/
accept-language: en-gb
accept-encoding: gzip, deflate
user-agent: Mozilla/4.0 ( compatible; MSIE 5.5; Windows NT 5.0)
host: localhost
connection: Keep-Alive
cookie: JSESSIONID= E0F9646772F4448004C16122020664F1
输出的应答头如下所示:
Content-Type: text/html;charset=8859_1 Content-Type: text/plain
如果进行网络跟踪,你会发现这里少了一些东西。例如,状态代码没有显示,这是因为有一些应答头由容器在过滤器链执行之前或之后设置。
过滤器有很多用途,比如验证、转换、加密/解密。但有一点必须注意:你可以把过滤器关联到任意资源,而不仅仅是Servlet。如果你使用的是一个插入到其他Web服务器的Servlet引擎,Web服务器很可能会不依赖于Servlet容器独立地提供服务。在这种情况下,Servlet容器将接收不到所有的请求,所以过滤器也就不会总是被执行。
JSP 1.1中有一个问题涉及到请求分派。如果你有一个页面执行include操作,jsp:include有一个必须设置为true的强制性flush(刷新)属性:
<jsp:include page="somePage" flush="true" />
这个属性强制容器把当前缓冲区内容刷新到客户端。这样,JSP页面不能再设置任意HTTP应答头。JSP 1.2规范已经修正这个问题,jsp:include标记中现在已经可以指定flush="false"。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -