更新時間:2022-01-04 09:52:45 來源:動力節點 瀏覽813次
Java Servlet 規范 2.3 版引入了一種新的組件類型,稱為過濾器。甲濾波器動態地攔截請求和響應,以變換或使用包含在請求或響應中的信息。過濾器本身通常不創建響應,而是提供可以“附加”到任何類型的 servlet 或 JSP 頁面的通用功能。
出于多種原因,過濾器很重要。首先,它們提供了將重復性任務封裝在可重用單元中的能力。有組織的開發人員一直在尋找模塊化代碼的方法。模塊化代碼更易于管理和記錄,更易于調試,如果做得好,可以在其他設置中重用。
其次,過濾器可用于轉換來自 servlet 或 JSP 頁面的響應。Web 應用程序的一個常見任務是格式化發送回客戶端的數據。客戶端越來越需要 HTML 以外的格式(例如 WML)。為了適應這些客戶,在功能齊全的 Web 應用程序中通常有一個強大的轉換或過濾組件。許多 servlet 和 JSP 容器都引入了專有過濾器機制,這為部署在該容器上的開發人員帶來了收益,但降低了此類代碼的可重用性。隨著過濾器作為 Java Servlet 規范的一部分的引入,開發人員現在有機會編寫可跨容器移植的可重用轉換組件。
過濾器可以執行許多不同類型的功能。我們將討論本文中斜體項目的示例:
基于用戶身份的身份驗證阻止請求。
日志記錄和審計 -跟蹤 Web 應用程序的用戶。
圖像轉換-縮放貼圖等。
數據壓縮 -使下載更小。
本地化 -針對特定語言環境的請求和響應。
XML 內容的 XSL/T 轉換 - 將Web 應用程序響應定位到不止一種類型的客戶端。
這些只是過濾器的一些應用。還有更多,例如加密、標記化、觸發資源訪問事件、mime 類型鏈接和緩存。
在本文中,我們將首先討論如何編寫過濾器來執行以下類型的任務:
查詢請求并采取相應措施
阻止請求和響應對進一步傳遞。
修改請求頭和數據。您可以通過提供請求的自定義版本來完成此操作。
修改響應頭和數據。您可以通過提供自定義版本的響應來完成此操作。
我們將概述過濾器 API,并描述如何開發定制的請求和響應。
對過濾器進行編程只是使用過濾器的一半工作——當應用程序部署在 Web 容器中時,您還需要配置它們如何映射到 servlet。編程和配置的這種分離是過濾機制的主要好處:
您無需重新編譯任何內容即可更改 Web 應用程序的輸入或輸出。您只需編輯文本文件或使用工具來更改配置。例如,向 PDF 下載添加壓縮只是將壓縮過濾器映射到下載 servlet 的問題。
您可以輕松地試驗過濾器,因為它們非常易于配置。
本文的最后一部分展示了如何使用非常靈活的過濾器配置機制。閱讀本文后,您將掌握實現自己的過濾器的知識,并掌握一些基于一些常見過濾器類型的實用技巧。
該過濾器API由定義Filter,FilterChain和FilterConfig在所述接口javax.servlet包。您可以通過實現Filter 接口來定義過濾器 。由容器傳遞給過濾器的過濾器鏈提供了調用一系列過濾器的機制。過濾器配置包含初始化數據。
Filter接口中最重要的方法是doFilter方法,它是過濾器的核心。此方法通常執行以下一些操作:
檢查請求標頭
如果希望修改請求標頭或數據或完全阻止請求,則自定義請求對象
如果希望修改響應頭或數據,則自定義響應對象
調用過濾器鏈中的下一個實體。如果當前過濾器是鏈中以目標 servlet 結尾的最后一個過濾器,則下一個實體是鏈末尾的資源;否則,它是在 WAR 中配置的下一個過濾器。它通過調用doFilter鏈對象上的方法來調用下一個實體(傳入調用它的請求和響應,或者它可能創建的包裝版本)。或者,它可以選擇通過不調用下一個實體來阻止請求。在后一種情況下,過濾器負責填寫響應。
在調用鏈中的下一個過濾器后檢查響應頭
拋出異常以指示處理中的錯誤
除了doFilter,您還必須實現init和destroy方法。it容器在實例化過濾器時調用in方法。如果您希望將初始化參數傳遞給過濾器,您可以從FilterConfig傳遞給的對象中檢索它們init。
現在您已經知道過濾器 API 的主要元素是什么,讓我們來看看一個非常簡單的過濾器,它不會阻止請求、轉換響應或任何花哨的東西——這是開始學習 API 基本概念的好地方。
考慮跟蹤用戶數量的網站。要將此功能添加到現有 Web 應用程序而不更改任何 servlet,您可以使用日志過濾器。
HitCounterFilter在訪問 servlet 時遞增并記錄計數器的值。在該doFilter方法中,HitCounterFilter首先從過濾器配置對象中檢索 servlet 上下文,以便它可以訪問存儲為上下文屬性的計數器。在過濾器檢索、遞增并將計數器寫入日志后,它會調用doFilter傳遞給原始doFilter方法的過濾器鏈對象。省略的代碼在編程定制的請求和響應中討論 。
public final class HitCounterFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig)
throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (filterConfig == null)
return;
StringWriter sw = new StringWriter();
PrintWriter writer = new PrintWriter(sw);
Counter counter = (Counter)filterConfig.
getServletContext().
getAttribute("hitCounter");
writer.println();
writer.println("===============");
writer.println("The number of hits is: " +
counter.incCounter());
writer.println("===============");
// Log the resulting string
writer.flush();
filterConfig.getServletContext().
log(sw.getBuffer().toString());
...
chain.doFilter(request, wrapper);
...
}
}
目前,很多瀏覽器在 HTTP 請求的 Content-Type 頭中不發送字符編碼信息。如果客戶端請求未指定編碼,則容器使用默認編碼來解析請求參數。如果客戶端沒有設置字符編碼并且請求參數的編碼與默認編碼不同,則參數將被錯誤解析。您可以使用該方法setCharacterEncoding在ServletRequest接口設置編碼。由于必須在解析任何發布數據或從請求中讀取任何輸入之前調用此方法,因此此函數是過濾器的主要應用程序。
這種過濾器包含在隨Tomcat 4.0 Web 容器一起分發的示例 中。過濾器從過濾器初始化參數設置字符編碼。這個過濾器可以很容易地擴展到根據傳入請求的特征設置編碼,例如 Accept-Language 和 User-Agent 標頭的值,或者保存在當前用戶會話中的值。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) throws
IOException, ServletException {
String encoding = selectEncoding(request);
if (encoding != null)
request.setCharacterEncoding(encoding);
chain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws
ServletException {
this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
}
protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
到目前為止,我們已經看過一些簡單的例子。現在讓我們更復雜一點,看看一個過濾器,它修改來自客戶端的請求或響應返回給客戶端。過濾器可以通過多種方式修改請求或響應。例如,過濾器可以向請求添加一個屬性,或者它可以在響應中插入數據或以其他方式轉換響應。
修改響應的過濾器通常必須在響應返回給客戶端之前捕獲響應。這樣做的方法是將生成響應的 servlet 傳遞給一個替代流。替代流可防止 servlet 在完成時關閉原始響應流,并允許過濾器修改 servlet 的響應。
為了將此替代流傳遞給 servlet,過濾器創建了一個響應“包裝器”,該響應“包裝器”覆蓋getWriterorgetOutputStream方法以返回此替代流。包裝器被傳遞給doFilter過濾器鏈的方法。包裝方法默認調用包裝的請求或響應對象。這種方法遵循設計模式,可重用面向對象軟件的元素中描述的著名的包裝器或裝飾器模式。以下部分描述了前面描述的命中計數器過濾器和其他類型的過濾器如何使用包裝器。
要覆蓋請求方法,請將請求包裝在一個擴展 或的對象中。要覆蓋響應方法,請將響應包裝在一個擴展 或 的對象中 。 ServletRequestWrapperHttpServletRequestWrapperServletResponseWrapperHttpServletResponseWrapper編程過濾器中描述的命中計數器過濾 器將計數器的值插入到響應中。省略的代碼HitCounterFilter是:
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper(
(HttpServletResponse)response);
chain.doFilter(request, wrapper);
if(wrapper.getContentType().equals("text/html")) {
CharArrayWriter caw = new CharArrayWriter();
caw.write(wrapper.toString().substring(0,
wrapper.toString().indexOf("</body>")-1));
caw.write("<p>\nYou are visitor number
<font color='red'>" + counter.getCounter() + "</font>");
caw.write("\n</body></html>");
response.setContentLength(caw.toString().length());
out.write(caw.toString());
} else
out.write(wrapper.toString());
out.close();
HitCounterFilter將響應包裝在一個CharResponseWrapper. CharResponseWrapper覆蓋該getWriter方法以返回一個替代流,過濾器鏈末尾的 servlet 將其響應寫入該替代流。當chain.doFilter返回時,HitCounterFilter檢索來自servlet的響應PrintWriter,并將其寫入緩沖區,如果它是一個HTML的響應。過濾器將計數器的值插入緩沖區,重置響應的內容長度頭,最后將緩沖區的內容寫入響應流。
public class CharResponseWrapper extends
HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response){
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter(){
return new PrintWriter(output);
}
}
修改響應的過濾器的另一個示例是隨 Tomcat servlet 引擎分發的示例中包含的壓縮過濾器。盡管高速 Internet 連接變得越來越普遍,但仍然需要有效地使用帶寬。壓縮過濾器很方便,因為您可以將它附加到任何 servlet 以減小響應的大小。
與命中計數器過濾器一樣,壓縮過濾器創建一個替代流,在本例中CompressionResponseStream,用于 servlet 寫入并包裝傳遞給 servlet 的響應。
僅當客戶端可以接受壓縮響應時,過濾器才會創建包裝器和替代流。servlet 將其響應寫入它從包裝器檢索的壓縮流。一旦數據大于作為初始化參數傳遞給過濾器的閾值,則CompressionResponseStream覆蓋將write響應數據寫入 a的方法GZIPOutputStream:
public void write(int b) throws IOException {
...
if ((bufferCount >= buffer.length) ||
(count>=compressionThreshold)) {
compressionThresholdReached = true;
}
if (compressionThresholdReached) {
writeToGZip(b);
} else {
buffer[bufferCount++] = (byte) b;
count++;
}
}
我們將討論的最后一個過濾器是 XSLT 過濾器。XSLT 是一種用于轉換 XML 數據的語言。您可以使用 XSLT 將 XML 文檔轉換為面向最終用戶的格式,例如 HTML 或 PDF,或另一種 XML 格式。一些示例應用包括:
將一家公司要求的格式的 XML 文檔轉換為另一家公司要求的格式。
根據用戶偏好自定義網頁的外觀。
通過查看 User-Agent 標頭并選擇樣式表,使同一 Web 應用程序能夠響應不同類型的客戶端,例如 WML 手機和 cHTML 手機。
考慮一個響應產品庫存請求的 Web 服務。以下 XML 文檔是此類響應的示例:
<book>
<isbn>123</isbn>
<title>Web Servers for Fun and Profit</title>
<quantity>10</quantity>
<price>$17.95</price>
</book>
以下 XSL 樣式表將此 XML 文檔呈現為 HTML 格式的面向用戶的庫存描述和 XML 格式的面向機器的版本。
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="book"> <html>
<body>There are <xsl:value-of select="quantity"/> copies of
<i><xsl:value-of select="title"/></i> available.
</body>
</html>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="no"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="book">
<xsl:element name="book">
<xsl:attribute name="isbn"><xsl:value-of select="isbn"/></
xsl:attribute>
<xsl:element name="quantity"><xsl:value-of select="quantity"/
></xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
以下 XSLT 過濾器使用樣式表根據請求參數的值轉換響應。過濾器根據請求參數設置響應的內容類型。然后將響應包裝在 a 中CharResponseWrapper并傳遞給doFilter過濾器鏈的方法。過濾器鏈中的最后一個元素是一個 servlet,它返回前面描述的庫存響應。當doFilter返回時,過濾器檢索來自包裝器和轉換所述響應數據它使用樣式表。
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String contentType;
String styleSheet;
String type = request.getParameter("type");
if (type == null || type.equals("")) {
contentType = "text/html";
styleSheet = "/xml/html.xsl";
} else {
if (type.equals("xml")) {
contentType = "text/plain";
styleSheet = "/xml/xml.xsl";
} else {
contentType = "text/html";
styleSheet = "/xml/html.xsl";
}
}
response.setContentType(contentType);
String stylepath=filterConfig.getServletContext().
getRealPath(styleSheet);
Source styleSource = new StreamSource(stylePath);
PrintWriter out = response.getWriter();
CharResponseWrapper responseWrapper =
new CharResponseWrapper(
(HttpServletResponse)response);
chain.doFilter(request, wrapper);
// Get response from servlet
StringReader sr = new StringReader(
new String(wrapper.getData()));
Source xmlSource = new StreamSource((Reader)sr);
try {
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
Transformer transformer = transformerFactory.
newTransformer(styleSource);
CharArrayWriter caw = new CharArrayWriter();
StreamResult result = new StreamResult(caw);
transformer.transform(xmlSource, result);
response.setContentLength(caw.toString().length());
out.write(caw.toString());
} catch(Exception ex) {
out.println(ex.toString());
out.write(wrapper.toString());
}
}
現在我們已經了解了如何編寫過濾器,最后一步是指定如何將其應用于一個 Web 組件或一組 Web 組件。要將過濾器映射到 servlet,您:
使用<filter>Web 應用程序部署描述符中的元素聲明過濾器。此元素為過濾器創建名稱并聲明過濾器的實現類和初始化參數。
通過<filter-mapping>在部署描述符中定義一個元素,將過濾器映射到 servlet 。此元素通過名稱或 URL 模式將過濾器名稱映射到 servlet。
以下元素顯示了如何指定壓縮過濾器所需的元素。要定義壓縮過濾器,您需要為過濾器提供一個名稱、實現過濾器的類以及閾值初始化參數的名稱和值。
<filter>
<filter-name>Compression Filter</filter-name>
<filter-class>CompressionFilter</filter-class>
<init-param>
<param-name>compressionThreshold</param-name>
<param-value>10</param-value>
</init-param>
</filter>
該filter-mapping元素將壓縮過濾器映射到 servlet CompressionTest。映射還可以指定 URL 模式/CompressionTest。請注意filter,filter-mapping、servlet、 和servlet-mapping元素必須按此順序出現在 Web 應用程序部署描述符中。
<filter-mapping>
<filter-name>Compression Filter</filter-name>
<servlet-name>CompressionTest</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>CompressionTest</servlet-name>
<servlet-class>CompressionTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CompressionTest</servlet-name>
<url-pattern>/CompressionTest</url-pattern>
</servlet-mapping>
請注意,此映射會導致過濾器被調用以處理對CompressionTestservlet和映射到 URL 模式的任何 servlet JSP 或靜態內容的所有請求/CompressionTest。
如果您想記錄對 Web 應用程序的每個請求,您可以將命中計數器過濾器映射到 URL 模式/*。這是與示例一起分發的部署描述符:
?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://bit.ly/15eRp2z">
<web-app>
<filter>
<filter-name>XSLTFilter</filter-name>
<filter-class>XSLTFilter</filter-class>
</filter>
<filter>
<filter-name>HitCounterFilter</filter-name>
<filter-class>HitCounterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HitCounterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>XSLTFilter</filter-name>
<servlet-name>FilteredFileServlet</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>FilteredFileServlet</servlet-name>
<servlet-class>FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FilteredFileServlet</servlet-name>
<url-pattern>/ffs</url-pattern>
</servlet-mapping>
</web-app>
如您所見,您可以將一個過濾器映射到一個或多個 servlet,也可以將多個過濾器映射到一個 servlet。這在圖 1中進行了說明,其中過濾器 F1 映射到 servlet S1、S2 和 S3,過濾器 F2 映射到 servlet S2,過濾器 F3 映射到 servlet S1 和 S2。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習