前言
在上篇博文中,我們已經(jīng)講解了過濾器的基本概念,使用以及簡單的Servlet應(yīng)用了。這篇博文主要講解過濾器的高級應(yīng)用。。
編碼過濾器
目的:解決全站的亂碼問題
開發(fā)過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//將request和response強轉(zhuǎn)成http協(xié)議的
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
httpServletRequest.setCharacterEncoding("UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("text/html;charset=UTF-8");
chain.doFilter(httpServletRequest, httpServletResponse);
}
第一次測試
Servlet1中向瀏覽器回應(yīng)中文數(shù)據(jù),沒有出現(xiàn)亂碼。
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("看完博客點贊!");
}
分析
上面的過濾器是不完善的,因為瀏覽器用get方式提交給服務(wù)器的中文數(shù)據(jù),單單靠上面的過濾器是無法完成的!
那么我們需要怎么做呢??我們之前解決get方式的亂碼問題是這樣的:使用request獲取傳遞過來的數(shù)據(jù),經(jīng)過ISO 8859-1反編碼獲取得到不是亂碼的數(shù)據(jù)(傳到Servlet上的數(shù)據(jù)已經(jīng)被ISO 8859-1編碼過了,反編碼就可以獲取原來的數(shù)據(jù)),再用UTF-8編碼,得到中文數(shù)據(jù)!
在Servlet獲取瀏覽器以GET方式提交過來的中文是亂碼的根本原因是:getParameter()方法是以ISO 8859-1的編碼來獲取瀏覽器傳遞過來的數(shù)據(jù)的,得到的是亂碼
既然知道了根本原因,那也好辦了:過濾器傳遞的request對象,使用getParameter()方法的時候,獲取得到的是正常的中文數(shù)據(jù)
也就是說,sun公司為我們提供的request對象是不夠用的,因為sun公司提供的request對象使用getParameter()獲取get方式提交過來的數(shù)據(jù)是亂碼,于是我們要增強request對象(使得getParameter()獲取得到的是中文)!
增強request對象
增強request對象,我們要使用包裝設(shè)計模式!
包裝設(shè)計模式的五個步驟:
- 1、實現(xiàn)與被增強對象相同的接口
- 2、定義一個變量記住被增強對象
- 3、定義一個構(gòu)造器,接收被增強對象
- 4、覆蓋需要增強的方法
- 5、對于不想增強的方法,直接調(diào)用被增強對象(目標(biāo)對象)的方法
sun公司也知道我們可能對request對象的方法不滿意,于是提供了HttpServletRequestWrapper類給我們實現(xiàn)(如果實現(xiàn)HttpServletRequest接口的話,要實現(xiàn)太多的方法了?。?/strong>
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if (value == null) {
return null;
}
//如果不是get方法的,直接返回就行了
if (!this.request.getMethod().equalsIgnoreCase("get")) {
return null;
}
try {
//進(jìn)來了就說明是get方法,把亂碼的數(shù)據(jù)
value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding());
return value ;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException("不支持該編碼");
}
}
}
將被增強的request對象傳遞給目標(biāo)資源,那么目標(biāo)資源使用request調(diào)用getParameter()方法的時候,獲取得到的就是中文數(shù)據(jù),而不是亂碼了!
//將request和response強轉(zhuǎn)成http協(xié)議的
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
httpServletRequest.setCharacterEncoding("UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("text/html;charset=UTF-8");
MyRequest myRequest = new MyRequest(httpServletRequest);
//傳遞給目標(biāo)資源的request是被增強后的。
chain.doFilter(myRequest, httpServletResponse);
第二次測試
- 使用get方式傳遞中文數(shù)據(jù)給服務(wù)器
<form action="${pageContext.request.contextPath}/Servlet1" method="get">
<input type="hidden" name="username" value="中國">
<input type="submit" value="提交">
</form>
敏感詞的過濾器
如果用戶輸入了敏感詞(傻b、尼瑪、操蛋等等不文明語言時),我們要將這些不文明用于屏蔽掉,替換成符號!
要實現(xiàn)這樣的功能也很簡單,用戶輸入的敏感詞肯定是在getParameter()獲取的,我們在getParameter()得到這些數(shù)據(jù)的時候,判斷有沒有敏感詞匯,如果有就替換掉就好了!簡單來說:也是要增強request對象
增強request對象
class MyDirtyRequest extends HttpServletRequestWrapper {
HttpServletRequest request;
//定義一堆敏感詞匯
private List<String> list = Arrays.asList("傻b", "尼瑪", "操蛋");
public MyDirtyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if (value == null) {
return null;
}
//遍歷list集合,看看獲取得到的數(shù)據(jù)有沒有敏感詞匯
for (String s : list) {
if (s.equals(value)) {
value = "*****";
}
}
return value ;
}
}
開發(fā)過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//將request和response強轉(zhuǎn)成http協(xié)議的
HttpServletRequest httpServletRequest = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest);
//傳送給目標(biāo)資源的是被增強后的request對象
chain.doFilter(dirtyRequest, httpServletResponse);
}
測試
壓縮資源過濾器
按照過濾器的執(zhí)行順序:執(zhí)行完目標(biāo)資源,過濾器后面的代碼還會執(zhí)行。所以,我們在過濾器中可以獲取執(zhí)行完目標(biāo)資源后的response對象!
我們知道sun公司提供的response對象調(diào)用write()方法,是直接把數(shù)據(jù)返回給瀏覽器的。我們要想實現(xiàn)壓縮的功能,write()方法就不能直接把數(shù)據(jù)寫到瀏覽器上!
這和上面是類似的,過濾器傳遞給目標(biāo)資源的response對象就需要被我們增強,使得目標(biāo)資源調(diào)用writer()方法的時候不把數(shù)據(jù)直接寫到瀏覽器上!
增強response對象
response對象可能會使用PrintWriter或者ServletOutputStream對象來調(diào)用writer()方法的,所以我們增強response對象的時候,需要把getOutputSteam和getWriter()重寫
class MyResponse extends HttpServletResponseWrapper{
HttpServletResponse response;
public MyResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return super.getOutputStream();
}
@Override
public PrintWriter getWriter() throws IOException {
return super.getWriter();
}
}
接下來,ServletOutputSteam要調(diào)用writer()方法,使得它不會把數(shù)據(jù)寫到瀏覽器上。這又要我們增強一遍了!
增強ServletOutputSteam
/*增強ServletOutputSteam,讓writer方法不把數(shù)據(jù)直接返回給瀏覽器*/
class MyServletOutputStream extends ServletOutputStream{
private ByteArrayOutputStream byteArrayOutputStream;
public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
this.byteArrayOutputStream = byteArrayOutputStream;
}
//當(dāng)調(diào)用write()方法的時候,其實是把數(shù)據(jù)寫byteArrayOutputSteam上
@Override
public void write(int b) throws IOException {
this.byteArrayOutputStream.write(b);
}
}
增強PrintWriter
PrintWriter對象就好辦了,它本來就是一個包裝類,看它的構(gòu)造方法,我們直接可以把ByteArrayOutputSteam傳遞給PrintWriter上。
@Override
public PrintWriter getWriter() throws IOException {
printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
return printWriter;
}
獲取緩存數(shù)據(jù)
我們把數(shù)據(jù)都寫在了ByteArrayOutputSteam上了,應(yīng)該提供方法給外界過去緩存中的數(shù)據(jù)!
public byte[] getBuffer() {
try {
//防止數(shù)據(jù)在緩存中,要刷新一下!
if (printWriter != null) {
printWriter.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
增強response的完整代碼
class MyResponse extends HttpServletResponseWrapper{
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
private PrintWriter printWriter ;
private HttpServletResponse response;
public MyResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
//這個的ServletOutputSteam對象調(diào)用write()方法的時候,把數(shù)據(jù)是寫在byteArrayOutputSteam上的
return new MyServletOutputStream(byteArrayOutputStream);
}
@Override
public PrintWriter getWriter() throws IOException {
printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
return printWriter;
}
public byte[] getBuffer() {
try {
//防止數(shù)據(jù)在緩存中,要刷新一下!
if (printWriter != null) {
printWriter.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
MyResponse myResponse = new MyResponse(response);
//把被增強的response對象傳遞進(jìn)去,目標(biāo)資源調(diào)用write()方法的時候就不會直接把數(shù)據(jù)寫在瀏覽器上了
chain.doFilter(request, myResponse);
//得到目標(biāo)資源想要返回給瀏覽器的數(shù)據(jù)
byte[] bytes = myResponse.getBuffer();
//輸出原來的大小
System.out.println("壓縮前:"+bytes.length);
//使用GZIP來壓縮資源,再返回給瀏覽器
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
gzipOutputStream.write(bytes);
//得到壓縮后的數(shù)據(jù)
byte[] gzip = byteArrayOutputStream.toByteArray();
System.out.println("壓縮后:" + gzip.length);
//還要設(shè)置頭,告訴瀏覽器,這是壓縮數(shù)據(jù)!
response.setHeader("content-encoding", "gzip");
response.setContentLength(gzip.length);
response.getOutputStream().write(gzip);
}
測試
- 在Servlet上輸出一大段文字:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd" +
"uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif" +
"hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd" +
"suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh" +
"dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids" +
"hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid" +
"shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids" +
"hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui" +
"dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh" +
"uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids" +
"fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid" +
"sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui" +
"dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh" +
"uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf" +
"huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus" +
"fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid" +
"usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf" +
"idusfhuidsfhuidshdsuifhsduifhsd" +
"uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff");
}
- 效果:
HTML轉(zhuǎn)義過濾器
只要把getParameter()獲取得到的數(shù)據(jù)轉(zhuǎn)義一遍,就可以完成功能了。
增強request
class MyHtmlRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyHtmlRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
return this.Filter(value);
}
public String Filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}
過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
MyHtmlRequest myHtmlRequest = new MyHtmlRequest(request);
//傳入的是被增強的request!
chain.doFilter(myHtmlRequest, response);
}
測試
jsp代碼:
<form action="${pageContext.request.contextPath}/Servlet1" method="post">
<input type="hidden" name="username" value="<h1>你好i好<h1>">
<input type="submit" value="提交">
</form>
Servlet代碼:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String value = request.getParameter("username");
response.getWriter().write(value);
}
緩存數(shù)據(jù)到內(nèi)存中
在前面我們已經(jīng)做過了,讓瀏覽器不緩存數(shù)據(jù)【驗證碼的圖片是不應(yīng)該緩存的】。
現(xiàn)在我們要做的是:緩存數(shù)據(jù)到內(nèi)存中【如果某個資源重復(fù)使用,不輕易變化,應(yīng)該緩存到內(nèi)存中】
這個和壓縮數(shù)據(jù)的Filter非常類似的,因為讓數(shù)據(jù)不直接輸出給瀏覽器,把數(shù)據(jù)用一個容器(ByteArrayOutputSteam)存起來。如果已經(jīng)有緩存了,就取緩存的。沒有緩存就執(zhí)行目標(biāo)資源!
增強response對象
class MyResponse extends HttpServletResponseWrapper {
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
private PrintWriter printWriter ;
private HttpServletResponse response;
public MyResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
//這個的ServletOutputSteam對象調(diào)用write()方法的時候,把數(shù)據(jù)是寫在byteArrayOutputSteam上的
return new MyServletOutputStream(byteArrayOutputStream);
}
@Override
public PrintWriter getWriter() throws IOException {
printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding()));
return printWriter;
}
public byte[] getBuffer() {
try {
//防止數(shù)據(jù)在緩存中,要刷新一下!
if (printWriter != null) {
printWriter.close();
}
if (byteArrayOutputStream != null) {
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
//增強ServletOutputSteam,讓writer方法不把數(shù)據(jù)直接返回給瀏覽器
class MyServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream byteArrayOutputStream;
public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
this.byteArrayOutputStream = byteArrayOutputStream;
}
//當(dāng)調(diào)用write()方法的時候,其實是把數(shù)據(jù)寫byteArrayOutputSteam上
@Override
public void write(int b) throws IOException {
this.byteArrayOutputStream.write(b);
}
}
過濾器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//定義一個Map集合,key為頁面的地址,value為內(nèi)存的緩存
Map<String, byte[]> map = new HashMap<>();
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//得到客戶端想要請求的資源
String uri = request.getRequestURI();
byte[] bytes = map.get(uri);
//如果有緩存,直接返回給瀏覽器就行了,就不用執(zhí)行目標(biāo)資源了
if (bytes != null) {
response.getOutputStream().write(bytes);
return ;
}
//如果沒有緩存,就讓目標(biāo)執(zhí)行
MyResponse myResponse = new MyResponse(response);
chain.doFilter(request, myResponse);
//得到目標(biāo)資源想要發(fā)送給瀏覽器的數(shù)據(jù)
byte[] b = myResponse.getBuffer();
//把數(shù)據(jù)存到集合中
map.put(uri, b);
//把數(shù)據(jù)返回給瀏覽器
response.getOutputStream().write(b);
}
測試
盡管是刷新,獲取得到的也是從緩存拿到的數(shù)據(jù)!
如果文章有錯的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章的同學(xué),可以關(guān)注微信公眾號:Java3y