过滤器第二篇编码、敏感词、压缩、转义过滤器

前言在上篇博文中,我们已经讲解了过滤器的基本概念,使用以及简单的Servlet应用了。这篇博文主要讲解过滤器的高级应用。。编码过滤器目的:解决全站的乱码问题开发过滤器

publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsServletException,IOException{

//将request和response强转成http协议的

HttpServletRequesthttpServletRequest=(HttpServletRequest)req;

HttpServletResponsehttpServletResponse=(HttpServletResponse)resp;

httpServletRequest.setCharacterEncoding("UTF-8");

httpServletResponse.setCharacterEncoding("UTF-8");

httpServletResponse.setContentType("text/html;charset=UTF-8");

chain.doFilter(httpServletRequest,httpServletResponse);

}

第一次测试Servlet1中向浏览器回应中文数据,没有出现乱码。

protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{

response.getWriter().write("看完博客点赞!");

}

分析上面的过滤器是不完善的,因为浏览器用get方式提交给服务器的中文数据,单单靠上面的过滤器是无法完成的!那么我们需要怎么做呢??我们之前解决get方式的乱码问题是这样的:使用request获取传递过来的数据,经过ISO 8859-1反编码获取得到不是乱码的数据(传到Servlet上的数据已经被ISO 8859-1编码过了,反编码就可以获取原来的数据),再用UTF-8编码,得到中文数据!在Servlet获取浏览器以GET方式提交过来的中文是乱码的根本原因是:getParameter()方法是以ISO 8859-1的编码来获取浏览器传递过来的数据的,得到的是乱码既然知道了根本原因,那也好办了:过滤器传递的request对象,使用getParameter()方法的时候,获取得到的是正常的中文数据也就是说,sun公司为我们提供的request对象是不够用的,因为sun公司提供的request对象使用getParameter()获取get方式提交过来的数据是乱码,于是我们要增强request对象(使得getParameter()获取得到的是中文)!增强request对象增强request对象,我们要使用包装设计模式!包装设计模式的五个步骤:

1、实现与被增强对象相同的接口

2、定义一个变量记住被增强对象

3、定义一个构造器,接收被增强对象

4、覆盖需要增强的方法

5、对于不想增强的方法,直接调用被增强对象(目标对象)的方法

sun公司也知道我们可能对request对象的方法不满意,于是提供了HttpServletRequestWrapper类给我们实现(如果实现HttpServletRequest接口的话,要实现太多的方法了!)

classMyRequestextendsHttpServletRequestWrapper{

privateHttpServletRequestrequest;

publicMyRequest(HttpServletRequestrequest){

super(request);

this.request=request;

}

@Override

publicStringgetParameter(Stringname){

Stringvalue=this.request.getParameter(name);

if(value==null){

returnnull;

}

//如果不是get方法的,直接返回就行了

if(!this.request.getMethod().equalsIgnoreCase("get")){

returnnull;

}

try{

//进来了就说明是get方法,把乱码的数据

value=newString(value.getBytes("ISO8859-1"),this.request.getCharacterEncoding());

returnvalue;

}catch(UnsupportedEncodingExceptione){

e.printStackTrace();

thrownewRuntimeException("不支持该编码");

}

}

}

将被增强的request对象传递给目标资源,那么目标资源使用request调用getParameter()方法的时候,获取得到的就是中文数据,而不是乱码了!

//将request和response强转成http协议的

HttpServletRequesthttpServletRequest=(HttpServletRequest)req;

HttpServletResponsehttpServletResponse=(HttpServletResponse)resp;

httpServletRequest.setCharacterEncoding("UTF-8");

httpServletResponse.setCharacterEncoding("UTF-8");

httpServletResponse.setContentType("text/html;charset=UTF-8");

MyRequestmyRequest=newMyRequest(httpServletRequest);

//传递给目标资源的request是被增强后的。

chain.doFilter(myRequest,httpServletResponse);

第二次测试

使用get方式传递中文数据给服务器

敏感词的过滤器如果用户输入了敏感词(傻b、尼玛、操蛋等等不文明语言时),我们要将这些不文明用于屏蔽掉,替换成符号!要实现这样的功能也很简单,用户输入的敏感词肯定是在getParameter()获取的,我们在getParameter()得到这些数据的时候,判断有没有敏感词汇,如果有就替换掉就好了!简单来说:也是要增强request对象增强request对象

classMyDirtyRequestextendsHttpServletRequestWrapper{

HttpServletRequestrequest;

//定义一堆敏感词汇

privateListlist=Arrays.asList("傻b","尼玛","操蛋");

publicMyDirtyRequest(HttpServletRequestrequest){

super(request);

this.request=request;

}

@Override

publicStringgetParameter(Stringname){

Stringvalue=this.request.getParameter(name);

if(value==null){

returnnull;

}

//遍历list集合,看看获取得到的数据有没有敏感词汇

for(Strings:list){

if(s.equals(value)){

value="*****";

}

}

returnvalue;

}

}

开发过滤器

publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsServletException,IOException{

//将request和response强转成http协议的

HttpServletRequesthttpServletRequest=(HttpServletRequest)req;

HttpServletResponsehttpServletResponse=(HttpServletResponse)resp;

MyDirtyRequestdirtyRequest=newMyDirtyRequest(httpServletRequest);

//传送给目标资源的是被增强后的request对象

chain.doFilter(dirtyRequest,httpServletResponse);

}

测试压缩资源过滤器按照过滤器的执行顺序:执行完目标资源,过滤器后面的代码还会执行。所以,我们在过滤器中可以获取执行完目标资源后的response对象!我们知道sun公司提供的response对象调用write()方法,是直接把数据返回给浏览器的。我们要想实现压缩的功能,write()方法就不能直接把数据写到浏览器上!这和上面是类似的,过滤器传递给目标资源的response对象就需要被我们增强,使得目标资源调用writer()方法的时候不把数据直接写到浏览器上!增强response对象response对象可能会使用PrintWriter或者ServletOutputStream对象来调用writer()方法的,所以我们增强response对象的时候,需要把getOutputSteam和getWriter()重写

classMyResponseextendsHttpServletResponseWrapper{

HttpServletResponseresponse;

publicMyResponse(HttpServletResponseresponse){

super(response);

this.response=response;

}

@Override

publicServletOutputStreamgetOutputStream()throwsIOException{

returnsuper.getOutputStream();

}

@Override

publicPrintWritergetWriter()throwsIOException{

returnsuper.getWriter();

}

}

接下来,ServletOutputSteam要调用writer()方法,使得它不会把数据写到浏览器上。这又要我们增强一遍了!增强ServletOutputSteam

/*增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器*/

classMyServletOutputStreamextendsServletOutputStream{

privateByteArrayOutputStreambyteArrayOutputStream;

publicMyServletOutputStream(ByteArrayOutputStreambyteArrayOutputStream){

this.byteArrayOutputStream=byteArrayOutputStream;

}

//当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上

@Override

publicvoidwrite(intb)throwsIOException{

this.byteArrayOutputStream.write(b);

}

}

增强PrintWriterPrintWriter对象就好办了,它本来就是一个包装类,看它的构造方法,我们直接可以把ByteArrayOutputSteam传递给PrintWriter上。获取缓存数据我们把数据都写在了ByteArrayOutputSteam上了,应该提供方法给外界过去缓存中的数据!

publicbyte[]getBuffer(){

try{

//防止数据在缓存中,要刷新一下!

if(printWriter!=null){

printWriter.close();

}

if(byteArrayOutputStream!=null){

byteArrayOutputStream.flush();

returnbyteArrayOutputStream.toByteArray();

}

}catch(IOExceptione){

e.printStackTrace();

}

returnnull;

}

增强response的完整代码

classMyResponseextendsHttpServletResponseWrapper{

privateByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();

privatePrintWriterprintWriter;

privateHttpServletResponseresponse;

publicMyResponse(HttpServletResponseresponse){

super(response);

this.response=response;

}

@Override

publicServletOutputStreamgetOutputStream()throwsIOException{

//这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的

returnnewMyServletOutputStream(byteArrayOutputStream);

}

@Override

publicPrintWritergetWriter()throwsIOException{

printWriter=newPrintWriter(newOutputStreamWriter(byteArrayOutputStream,this.response.getCharacterEncoding()));

returnprintWriter;

}

publicbyte[]getBuffer(){

try{

//防止数据在缓存中,要刷新一下!

if(printWriter!=null){

printWriter.close();

}

if(byteArrayOutputStream!=null){

byteArrayOutputStream.flush();

returnbyteArrayOutputStream.toByteArray();

}

}catch(IOExceptione){

e.printStackTrace();

}

returnnull;

}

}

过滤器

publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsServletException,IOException{

HttpServletRequestrequest=(HttpServletRequest)req;

HttpServletResponseresponse=(HttpServletResponse)resp;

MyResponsemyResponse=newMyResponse(response);

//把被增强的response对象传递进去,目标资源调用write()方法的时候就不会直接把数据写在浏览器上了

chain.doFilter(request,myResponse);

//得到目标资源想要返回给浏览器的数据

byte[]bytes=myResponse.getBuffer();

//输出原来的大小

System.out.println("压缩前:"+bytes.length);

//使用GZIP来压缩资源,再返回给浏览器

ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();

GZIPOutputStreamgzipOutputStream=newGZIPOutputStream(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);

}

测试

在Servlet上输出一大段文字:

protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{

response.getWriter().write("fdshfidsuhfidusfhuidsfhuidshdsuifhsd"+

"uifhsduifffffdshfidsuhfidusfhuidsfhuidshdsuif"+

"hsduifhsduifffffdshfidsuhfidusfhuidsfhuidshd"+

"suifhsduifhsduifffffdshfidsuhfidusfhuidsfhuidsh"+

"dsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuids"+

"hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhuid"+

"shdsuifhsduifhsduiffdshfidsuhfidusfhuidsfhuids"+

"hdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfhui"+

"dshdsuifhsduifhsduifffffdshfidsuhfidusfhuidsfh"+

"uidshdsuifhsduifhsduifffffdshfidsuhfidusfhuids"+

"fhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhuid"+

"sfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfhui"+

"dsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusfh"+

"uidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidusf"+

"huidsfhuidshdsuifhsduifhsduifffffdshfidsuhfidus"+

"fhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhfid"+

"usfhuidsfhuidshdsuifhsduifhsduifffffdshfidsuhf"+

"idusfhuidsfhuidshdsuifhsduifhsd"+

"uifffffdshfidsuhfidusfhuidsfhuidshdsuifhsduifhsduifffffff");

}

效果:

HTML转义过滤器只要把getParameter()获取得到的数据转义一遍,就可以完成功能了。增强request

classMyHtmlRequestextendsHttpServletRequestWrapper{

privateHttpServletRequestrequest;

publicMyHtmlRequest(HttpServletRequestrequest){

super(request);

this.request=request;

}

@Override

publicStringgetParameter(Stringname){

Stringvalue=this.request.getParameter(name);

returnthis.Filter(value);

}

publicStringFilter(Stringmessage){

if(message==null)

return(null);

charcontent[]=newchar[message.length()];

message.getChars(,message.length(),content,);

StringBufferresult=newStringBuffer(content.length+50);

for(inti=;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());

}

}

过滤器

publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsServletException,IOException{

HttpServletRequestrequest=(HttpServletRequest)req;

HttpServletResponseresponse=(HttpServletResponse)resp;

MyHtmlRequestmyHtmlRequest=newMyHtmlRequest(request);

//传入的是被增强的request!

chain.doFilter(myHtmlRequest,response);

}

测试jsp代码:

Servlet代码:

protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{

Stringvalue=request.getParameter("username");

response.getWriter().write(value);

}

缓存数据到内存中在前面我们已经做过了,让浏览器不缓存数据【验证码的图片是不应该缓存的】。现在我们要做的是:缓存数据到内存中【如果某个资源重复使用,不轻易变化,应该缓存到内存中】这个和压缩数据的Filter非常类似的,因为让数据不直接输出给浏览器,把数据用一个容器(ByteArrayOutputSteam)存起来。如果已经有缓存了,就取缓存的。没有缓存就执行目标资源!增强response对象

classMyResponseextendsHttpServletResponseWrapper{

privateByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();

privatePrintWriterprintWriter;

privateHttpServletResponseresponse;

publicMyResponse(HttpServletResponseresponse){

super(response);

this.response=response;

}

@Override

publicServletOutputStreamgetOutputStream()throwsIOException{

//这个的ServletOutputSteam对象调用write()方法的时候,把数据是写在byteArrayOutputSteam上的

returnnewMyServletOutputStream(byteArrayOutputStream);

}

@Override

publicPrintWritergetWriter()throwsIOException{

printWriter=newPrintWriter(newOutputStreamWriter(byteArrayOutputStream,this.response.getCharacterEncoding()));

returnprintWriter;

}

publicbyte[]getBuffer(){

try{

//防止数据在缓存中,要刷新一下!

if(printWriter!=null){

printWriter.close();

}

if(byteArrayOutputStream!=null){

byteArrayOutputStream.flush();

returnbyteArrayOutputStream.toByteArray();

}

}catch(IOExceptione){

e.printStackTrace();

}

returnnull;

}

}

//增强ServletOutputSteam,让writer方法不把数据直接返回给浏览器

classMyServletOutputStreamextendsServletOutputStream{

privateByteArrayOutputStreambyteArrayOutputStream;

publicMyServletOutputStream(ByteArrayOutputStreambyteArrayOutputStream){

this.byteArrayOutputStream=byteArrayOutputStream;

}

//当调用write()方法的时候,其实是把数据写byteArrayOutputSteam上

@Override

publicvoidwrite(intb)throwsIOException{

this.byteArrayOutputStream.write(b);

}

}

过滤器

publicvoiddoFilter(ServletRequestreq,ServletResponseresp,FilterChainchain)throwsServletException,IOException{

//定义一个Map集合,key为页面的地址,value为内存的缓存

Mapmap=newHashMap();

HttpServletRequestrequest=(HttpServletRequest)req;

HttpServletResponseresponse=(HttpServletResponse)resp;

//得到客户端想要请求的资源

Stringuri=request.getRequestURI();

byte[]bytes=map.get(uri);

//如果有缓存,直接返回给浏览器就行了,就不用执行目标资源了

if(bytes!=null){

response.getOutputStream().write(bytes);

return;

}

//如果没有缓存,就让目标执行

MyResponsemyResponse=newMyResponse(response);

chain.doFilter(request,myResponse);

//得到目标资源想要发送给浏览器的数据

byte[]b=myResponse.getBuffer();

//把数据存到集合中

map.put(uri,b);

//把数据返回给浏览器

response.getOutputStream().write(b);

}

测试尽管是刷新,获取得到的也是从缓存拿到的数据!

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180206G0KOQZ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券