在上篇博文中,咱們已經講解了過濾器的基本概念,使用以及簡單的Servlet應用了。這篇博文主要講解過濾器的高級應用。。html
目的:解決全站的亂碼問題設計模式
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //將request和response強轉成http協議的 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中向瀏覽器迴應中文數據,沒有出現亂碼。瀏覽器
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("看完博客點贊!"); }
上面的過濾器是不完善的,由於瀏覽器用get方式提交給服務器的中文數據,單單靠上面的過濾器是沒法完成的!緩存
那麼咱們須要怎麼作呢??咱們以前解決get方式的亂碼問題是這樣的:使用request獲取傳遞過來的數據,通過ISO 8859-1反編碼獲取獲得不是亂碼的數據(傳到Servlet上的數據已經被ISO 8859-1編碼過了,反編碼就能夠獲取原來的數據),再用UTF-8編碼,獲得中文數據!服務器
參考我以前的博文:mp.weixin.qq.com/s?__biz=MzI…微信
在Servlet獲取瀏覽器以GET方式提交過來的中文是亂碼的根本緣由是:getParameter()方法是以ISO 8859-1的編碼來獲取瀏覽器傳遞過來的數據的,獲得的是亂碼app
既然知道了根本緣由,那也好辦了:過濾器傳遞的request對象,使用getParameter()方法的時候,獲取獲得的是正常的中文數據jsp
也就是說,sun公司爲咱們提供的request對象是不夠用的,由於sun公司提供的request對象使用getParameter()獲取get方式提交過來的數據是亂碼,因而咱們要加強request對象(使得getParameter()獲取獲得的是中文)!ide
加強request對象,咱們要使用包裝設計模式!post
包裝設計模式的五個步驟:
sun公司也知道咱們可能對request對象的方法不滿意,因而提供了HttpServletRequestWrapper類給咱們實現(若是實現HttpServletRequest接口的話,要實現太多的方法了!)
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 { //進來了就說明是get方法,把亂碼的數據 value = new String(value.getBytes("ISO8859-1"), this.request.getCharacterEncoding()); return value ; } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException("不支持該編碼"); } } }
將被加強的request對象傳遞給目標資源,那麼目標資源使用request調用getParameter()方法的時候,獲取獲得的就是中文數據,而不是亂碼了!
//將request和response強轉成http協議的 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); //傳遞給目標資源的request是被加強後的。 chain.doFilter(myRequest, httpServletResponse);
<form action="${pageContext.request.contextPath}/Servlet1" method="get"> <input type="hidden" name="username" value="中國"> <input type="submit" value="提交"> </form>
若是用戶輸入了敏感詞(傻b、尼瑪、操蛋等等不文明語言時),咱們要將這些不文明用於屏蔽掉,替換成符號!
要實現這樣的功能也很簡單,用戶輸入的敏感詞確定是在getParameter()獲取的,咱們在getParameter()獲得這些數據的時候,判斷有沒有敏感詞彙,若是有就替換掉就行了!簡單來講:也是要加強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集合,看看獲取獲得的數據有沒有敏感詞彙 for (String s : list) { if (s.equals(value)) { value = "*****"; } } return value ; } }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //將request和response強轉成http協議的 HttpServletRequest httpServletRequest = (HttpServletRequest) req; HttpServletResponse httpServletResponse = (HttpServletResponse) resp; MyDirtyRequest dirtyRequest = new MyDirtyRequest(httpServletRequest); //傳送給目標資源的是被加強後的request對象 chain.doFilter(dirtyRequest, httpServletResponse); }
按照過濾器的執行順序:執行完目標資源,過濾器後面的代碼還會執行。因此,咱們在過濾器中能夠獲取執行完目標資源後的response對象!
咱們知道sun公司提供的response對象調用write()方法,是直接把數據返回給瀏覽器的。咱們要想實現壓縮的功能,write()方法就不能直接把數據寫到瀏覽器上!
這和上面是相似的,過濾器傳遞給目標資源的response對象就須要被咱們加強,使得目標資源調用writer()方法的時候不把數據直接寫到瀏覽器上!
response對象可能會使用PrintWriter或者ServletOutputStream對象來調用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要調用writer()方法,使得它不會把數據寫到瀏覽器上。這又要咱們加強一遍了!
/*加強ServletOutputSteam,讓writer方法不把數據直接返回給瀏覽器*/ class MyServletOutputStream extends ServletOutputStream{ private ByteArrayOutputStream byteArrayOutputStream; public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) { this.byteArrayOutputStream = byteArrayOutputStream; } //當調用write()方法的時候,實際上是把數據寫byteArrayOutputSteam上 @Override public void write(int b) throws IOException { this.byteArrayOutputStream.write(b); } }
PrintWriter對象就好辦了,它原本就是一個包裝類,看它的構造方法,咱們直接能夠把ByteArrayOutputSteam傳遞給PrintWriter上。
@Override public PrintWriter getWriter() throws IOException { printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, this.response.getCharacterEncoding())); return printWriter; }
咱們把數據都寫在了ByteArrayOutputSteam上了,應該提供方法給外界過去緩存中的數據!
public byte[] getBuffer() { try { //防止數據在緩存中,要刷新一下! if (printWriter != null) { printWriter.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.flush(); return byteArrayOutputStream.toByteArray(); } } catch (IOException e) { e.printStackTrace(); } return null; }
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對象調用write()方法的時候,把數據是寫在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 { //防止數據在緩存中,要刷新一下! 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對象傳遞進去,目標資源調用write()方法的時候就不會直接把數據寫在瀏覽器上了 chain.doFilter(request, myResponse); //獲得目標資源想要返回給瀏覽器的數據 byte[] bytes = myResponse.getBuffer(); //輸出原來的大小 System.out.println("壓縮前:"+bytes.length); //使用GZIP來壓縮資源,再返回給瀏覽器 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); gzipOutputStream.write(bytes); //獲得壓縮後的數據 byte[] gzip = byteArrayOutputStream.toByteArray(); System.out.println("壓縮後:" + gzip.length); //還要設置頭,告訴瀏覽器,這是壓縮數據! response.setHeader("content-encoding", "gzip"); response.setContentLength(gzip.length); response.getOutputStream().write(gzip); }
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"); }
只要把getParameter()獲取獲得的數據轉義一遍,就能夠完成功能了。
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); }
在前面咱們已經作過了,讓瀏覽器不緩存數據【驗證碼的圖片是不該該緩存的】。
如今咱們要作的是:緩存數據到內存中【若是某個資源重複使用,不輕易變化,應該緩存到內存中】
這個和壓縮數據的Filter很是相似的,由於讓數據不直接輸出給瀏覽器,把數據用一個容器(ByteArrayOutputSteam)存起來。若是已經有緩存了,就取緩存的。沒有緩存就執行目標資源!
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對象調用write()方法的時候,把數據是寫在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 { //防止數據在緩存中,要刷新一下! if (printWriter != null) { printWriter.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.flush(); return byteArrayOutputStream.toByteArray(); } } catch (IOException e) { e.printStackTrace(); } return null; } } //加強ServletOutputSteam,讓writer方法不把數據直接返回給瀏覽器 class MyServletOutputStream extends ServletOutputStream { private ByteArrayOutputStream byteArrayOutputStream; public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) { this.byteArrayOutputStream = byteArrayOutputStream; } //當調用write()方法的時候,實際上是把數據寫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爲內存的緩存 Map<String, byte[]> map = new HashMap<>(); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; //獲得客戶端想要請求的資源 String uri = request.getRequestURI(); byte[] bytes = map.get(uri); //若是有緩存,直接返回給瀏覽器就好了,就不用執行目標資源了 if (bytes != null) { response.getOutputStream().write(bytes); return ; } //若是沒有緩存,就讓目標執行 MyResponse myResponse = new MyResponse(response); chain.doFilter(request, myResponse); //獲得目標資源想要發送給瀏覽器的數據 byte[] b = myResponse.getBuffer(); //把數據存到集合中 map.put(uri, b); //把數據返回給瀏覽器 response.getOutputStream().write(b); }
儘管是刷新,獲取獲得的也是從緩存拿到的數據!
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y