现在的位置: 首页 > 综合 > 正文

第十四天:过滤器

2018年05月17日 ⁄ 综合 ⁄ 共 10994字 ⁄ 字号 评论关闭

学员学完4个作用域后就可以讲解javabean与el表达式了,这是因为el大量使用javabean,接着监听器与4个作用域联系紧密,这时候接着就可以引出监听器,所以,监听器应该放在filter之前讲解。

 

 

1.先画浏览器--->访问Servlet的图,然后在中间加入一个拦截者,说清楚这个拦截在来回时都可以拦截,决定是否放行,放行时是否修改请求,回来后决定是否修改结果。

tomcat访问sevlet之间加一个拦截器。

如何学习到在web.xml里面可以配置什么,在标签上面按F2就可以看到它的DTD限制,根据DTD写XML这很是重要的。

 

DoFilter就是go on.

在拦截器中,可以把request和response的数据给修改后然后再交给真正的servlet处理。

 

补充:Web.xml中的context-param与init-param  

context-paramweb
应用全局的数据。由ServletContext管理。
init-param
是指每个servlet自己的初始化参数,一般servlet自己定义。
通过servlet对象自己的getInitParameter("name")方法得到。

<context-param>
       <param-name>global</param-name>

       <param-value>global value aaa</param-value>
    </context-param>

 

  <servlet>
       <servlet-name>dwr-invoker</servlet-name>

       <display-name>DWR Servlet</display-name>
        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
        <init-param>
           <param-name>debug</param-name>

           <param-value>true</param-value>
       </init-param>
   </servlet>

System.out.println("Servletparam: "  + this.getInitParameter("initparam"));
 System.out.println("Globalparam: "+ this.getServletContext().getInitParameter("global"));

 

Element : filter

Content Model : (((description*, display-name*,icon*)),

 filter-name, filter-class, init-param*)

Element : filter-mapping

Content Model : (filter-name, (url-pattern |servlet-name)

 +, dispatcher*)

通过F2看它的DTD,然后根据DTD写出XML文件太重要了。

 

既然filter是在servlet之前的,那么它与servlet应该是极其相似的,我们学过
servelt中有init(ServletCofig config)方法,
init(FilterConfig filterConfig)

它也有,所以我们类比学习,它应该有一样的方法,

 

 

2.先演示放行和不放行的效果,然后演示在放行前后,分别在response中写入点东西,再说编写Servlet时不要把PrintWriter关闭,否则,filter就无法在后面添加新东西了。

是Servlet更强,还是filter更厉害?

 

    response.setContentType("text/html;charset=gbk");

       response.getWriter().println(filterConfig.getInitParameter("test"));

    chain.doFilter(request,response);

    response.getWriter().println(request.getRemotePort());

 

filter原理:filterservelt功能一样它是拦截在tomcatservelt之间的一道墙,可以代替serlvet的功能对tomcat进行收发请求,它可以给服务器一个response,服务器并不知道是谁发来的,会处理,这两个requestresponse始终还是这两个,它不过就是先经过了filter再经过了servlet,然后后来到了真正的servelt,在它回来的时间,它又经过这个过滤器。始终记住一句:过滤器永远不变,变化的是request
response
,不断的在服务器、filterserlvet之间游走。

 

3.filter放行静态网页后,修改filter内部的写response的代码,看到的还是原来的显示效果,让大家分析原因,修改静态网页后,才能看到filter内部写的response的效果

因为静态网页,你访问一次后,会有缓存,服务器这边是304状态码表示内容没有改变,那么就会不再改变,我们只要随便加个空格,让它改变,服务器会重新发给客户端,而不会用用户的缓存。

看到一个filter的运行效果后,提问filter有什么好处呢?例如,用Filter对资源的输入信息和输出结果进行控制和转换,对整个系统页面进行装饰,不用修改已有的大量Servlet程序,jsp页面和html页面。

 

例如我们想在一万个网页加上一个版权图标?用filter.

 

缓存就是客户端在访问服务器的时间,把最后改变的文件时间发给服务器,服务器发现一样就不再传数据给它,而是返回一个304状态码,说你可以用缓存。

 

 

 

servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求和<%@ include file="/index.jsp"%>的情况。

到了servlet-2.4Filter默认下只拦截外部提交的请求,forwardinclude这些内部转发都不会被过滤,但是有时候我们需要forward的时候也用到Filter,这样就需要如下配置。默认是request.

也就是说,有时间我们内部的forward转发和include,甚至出错都会想要触发filter,就可以在这里配置。

<filter>

<filter-name>TestFilter</filtername>

<filter-class>anni.TestFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>TestFilter</filtername>

<url-pattern>/*</url-pattern>

<dispatcher>REQUEST</dispatcher>

<dispatcher>FORWARD</dispatcher>

<dispatcher>INCLUDE</dispatcher>

<dispatcher>EXCEPTION</dispatcher>

</filter-mapping>

这里FORWARD是解决request.getDispatcher("index.jsp").forward(request,response);无法触发Filter的关键,配置上这个以后再进行forward的时候就可以触发过滤器了。

 

 

3.5讲解注册Filter时的dispatcher元素的4种取值,接着提问请求是给了一个Servlet,这个Servlet又跳转到了某个jsp页面,当Servlet被如下doFilter代码拦截时:

System.out.println(xixi);

response.setContentType(text/html;charset=gb2312);

out.println(haha);

doFileter();

out.println(hehe);

为什么xixi在命令行窗口中打印出来了,但haha与hehe并没有在jsp页面中打印出来,但是jsp中的乱码问题却因为response.setContentType(“text/html;charset=gb2312”);这条语句而解决。这是因为过滤器不拦截forward到的jsp页面,而只是拦截了初始请求的Servlet,其中的out.println相当于是写在Servlet中一样的,forward转发会清除响应体和阻止转发后的写入,但是在servlet中设置的response的头部分仍然保持有效。

再提问,JSP页面中的page指令设置errorPage属性也会导致跳转到一个错误处理页面,这是否会被<dispather>元素注册为error类型的Filter拦截呢?不会,因为这时候还是对应的是FORWARD

 

<%@ page errorPage=/error.jsp%>属于forward跳转。服务器内部的跳转。

 

4.FilterServlet的比较,Servlet能干的事情Filter都可以干,比Servlet功能更强。

 

5.使用Filter进行新闻页面的访问权限管理,新闻超链接(巩俐又结婚了,张柏芝又有新照片了,刘晓庆又出嫁了等等),登录页面作为Filter参数由用户自己配置。

 

如果按照MVC模式,应该先访问一个servelt跳转到jsp页面。

Html页面解决相对路径问题:

<base
href
="http://localhost:8888/servletday7/">

 

getRequestURI

public java.lang.String getRequestURI()

Returns the partof this request's URL from the protocol name up to the query string in thefirst line of the HTTP request. The web container does not decode this String.For example:

First line of HTTP request

Returned Value

POST /some/path.html HTTP/1.1

/some/path.html

GET http://foo.bar/a.html HTTP/1.0

/a.html

HEAD /xyz?a=b HTTP/1.1

/xyz

To reconstruct an URL with a scheme and host, use HttpUtils.getRequestURL(javax.servlet.http.HttpServletRequest).

Returns:

a String containing the part of the URL from theprotocol name up to the query string

返回的是从HTTP协议里面截取的,从根目录下开头到请求参数截止。

getQueryString

public java.lang.String getQueryString()

Returns the querystring that is contained in the request URL after the path. This method returnsnull if the URL does not have a query string. Same as thevalue of the CGI variable QUERY_STRING.

Returns:

a String containing the query string or null if the URLcontains no query string. The value is not decoded by the container.

而这个方法返回的就是从参数开始的,这两个结合使用就拼接出了一个完整的路径。

 

Request.setCharaterEncoding();对POST方式起作用。

如果换成对下载资源的管理,本系统就可以换个名称,美其名曰“站点下载管理系统”了。比较关键的代码如下:

                     StringsrcPath = req.getRequestURI().substring(

                                   req.getContextPath().length())

                                   +(req.getQueryString()==null ? "" : "?" + req.getQueryString());

                     System.out.println(srcPath);

                     session.setAttribute("srcPath",srcPath);

 

6.编写一个专门处理中文字符乱码的Filter,这里要在写Filter之前,先回顾和再现一下字符乱码的现象和直接在Servlet中的解决办法,在一个Servlet中编写doGet和doPost方法,先解决doPost的问题,再解释request.setCharacterEncoding方法对get方式的乱码不起作用,get方式要进行转码处理(实验前注意查看下Connector的配置情况),最后再写Filter和将Filter应用上去。

解决post乱码:----setCharacterEncoding只对post方式起作用。

response.setContentType("text/html;charset=utf-8");

       request.setCharacterEncoding("UTF-8");//与前台编码一致

       String username = request.getParameter("username");

        PrintWriter out =response.getWriter();

       out.println(username);

//     get

       response.setContentType("text/html;charset=utf-8");

       String
username
= request.getParameter("username");

       username=
new String(username.getBytes("iso8859-1"),"UTF-8");

       PrintWriter out = response.getWriter();

       out.println(username);

 

惊天秘密:用户可以发请求,也可接收响应,但它都是被动的,servlet里面设计的requestresponse这两个对象,都是让服务器来用的,用户是可以发请求,但是request是让服务器接收用户请求的,用户是可能接受响应,不过,response是让服务器给用户发响应的。

 

 

   

 

 

request.setCharacterEncoding("UTF-8");

      

       HttpServletRequest req = (HttpServletRequest) request;

       if (req.getMethod().equalsIgnoreCase("get")) {

           req = new ItcastServletRequest((HttpServletRequest)request,
encoding);

       }

       //如果response则传的就是真实的两个请求,如果是get,那么是把req包装以后再传给servlet,也就是说期间它修改的request

       //那么在sevlet里面调用request调用的就是我们修改过的request,我们再重写它可能调用的几个方法,不就实现了我们的目标。

       chain.doFilter(req,response);

 

 

Alt + shift + R.:统一改名

 

7.用Filter实现站点内容生成系统,提高网站性能的页面缓存技术。先演示一个附带不同的id参数访问某个jsp页面,返回不同的结果页面的情况(中间sleep模拟要花费3秒钟才能完成响应),静态页面缓存技术。缓存到硬盘上。第二次访问时间就快了。

提高网站性能:第一次从数据库里面查,第二次就缓存成一个html页面,这样能够提高网站的性能,这也就是站点内容生成系统。

提高网站内容的页面缓存技术

 

这个东西不仅可以提升页面缓存,而且可以通过循环做成一个站点管理系统。

 

 

为了保证它每次访问时,判断服务器有没有修改,加个参数。

 

 

 

Filter的核心代码如下:

       public void doFilter(...)

              HttpServletRequest req = (HttpServletRequest)request;

              String pid = req.getParameter("pid");

              //?pid=1 --->/products/pid_1.html

              String htmlUrl = "/products/pid_" + pid +".html";//把这个变成一个方法,便于扩展

              String htmlFilePath = req.getRealPath(htmlUrl);

              java.io.File file = new File(htmlFilePath);

              File dir = new File(file.getParent());

              if(!dir.isDirectory())

                            dir.mkdirs();

              System.out.println(dir.getPath());

              if(!file.exists()){

                     ItcastHttpServletResponse res = newItcastHttpServletResponse(

                                          (HttpServletResponse)response,file);

                     chain.doFilter(request, res);

              //这里一定要记住把假盒子的writer关闭,讲课时则可以先不关,锻炼学员解决问题的能力。

                     res.getWriter().close();

              }

              req.getRequestDispatcher(htmlUrl).forward(req,response);

       }

备注:HttpServletResponseWrapper中的getWriter方法不是建立在getOutputStream方法返回的OutputStream基础之上,所以,通常情况下对这两个方法都要重新编写。

-----------------

       privateclass ItcastHttpServletResponse extends HttpServletResponseWrapper{

              PrintWriterpw = null;         

              privateFile file = null;

              publicItcastHttpServletResponse(HttpServletResponse response,File file) {

                     super(response);

                     this.file= file;

                     // TODO Auto-generated constructorstub

              }

             

              @Override

              publicPrintWriter getWriter() throws IOException {

              /*刚开始把pw定义成了局部变量,jsp用的printwriter和filter中用的printwriter就不同了,

              所以,看到不到正确的效果,以后讲课时故意犯这种错误,反而更有利于锻炼学员分析问题和解决问题的能力。*/

                     System.out.println("becalling!");

                     if(pw==null)

                            pw= new PrintWriter(file); 

                     returnpw;

              }

             

       }

爱思考的学员提问:数据更新后怎么办?再访问一下这个路径,并带上一个特殊的参数即可。

 

提升网站缓存技术其实是这样的,我们要访问http://localhost:8888/day14_servlet_filter/product/product.jsp?pid=33这个页面,如果要硬盘上找到并且没有发生更改,则用硬盘上的,如果更改,则重新生成再放回硬盘

 

还有一个问题,就是一个页面中只有很少一部分要变,怎么办?如下载次数。

做成静态网页以后,怎么办?

 

<%!int 
count = 0;%>

document.write('<%= ++count%>');

 

然后在要使用的JSP里面通过 js插入进去,十分有效,十分有用。

<script
src
="${pageContext.request.contextPath}/count.jsp"></script><br/>

 

 

 

userName.matches("^(\\d|[a-zA-Z]|[\u4e00-\u9fa5]){1,10}$")用正则表达式判断字符串十分有用。

 

chatroom含有中文用户名的被踢出。

 

惊天大秘密:凡是以get方式,如点击超链接这种,里面有中文的,也就是在URL里面有中文的,首先必须对它埋URLEncoding.encoding(“中国”,”utf-8”)转码,如果它是直接发送给浏览器,那么就不会出现乱码,如果这个含有中文的请求再次请求了服务器,那么你就要进行解决乱码的操作,导入我弄好的一个jar包,然后再配置过滤器即可。十分完美

  <filter>

    <filter-name>chieseFilter</filter-name>

    <filter-class>cn.filter.ChineseFilter</filter-class>

    <init-param>

       
<
param-name>encoding</param-name>

       
<
param-value>UTF-8</param-value>

    </init-param>

  </filter>

这个表示告诉servelt将用什么解码。

 

 

 setHeader(name, value)如果Header中没有定义则添加,如果已定义则用新的value覆盖原用value值。

addHeader(namevalue)如果Header中没有定义则添加,如果已定义则保持原有value不改变。

 

 

8.过滤器输出网页文档的页眉与页脚,可以统一和经常更新页面的页眉和页脚了,自己做个类似sitemesh的东西,然后引出sitemesh。

 

9.单点登录系统。

       先演示运行效果,让大家有个直观的印象。

       先把没有filter时的运行效果做出来,即直接访问登录页面(分为已登录和未登录两种状态),直接访问成功页面(也分为已登录和未登录两种状态),再做登录和注销程序,最后再做Filter。

       Filter首先完成两星期自动登录,再改成单点登录系统。

 

作业练习:

 1. 用Filter跟踪用户上次访问站点的时间是最好的解决方案了,因为是在Session.isNew时才需要将本次访问时间通过Cookie写入到客户端,不知道用户首先会访问哪个Servlet,每个Servlet程序都进行这种判断和写入就太烦琐了。提示:这里的用户不需要登录,一个浏览器代表一个用户,把Cookie用持久化的方式存储在客户端。

 2.用Filter实现统计站内各个页面的访问次数。

 3.用Filter实现某些资源自能被某些IP地址访问,否则拒绝访问。

 4.用Filter实现下载系统的集中防盗链。

 5.在《核心基础》的4.5.7节中有这样一段描述:“因为HEAD请求方式除了不要求WEB服务器返回实体正文部分外,它的操作与GET方式完全一样,所以只要覆盖了doGet方法,即使没有覆盖doHead方法,Servlet也可以支持HEAD请求方式,但直接覆盖doHead方法对HEAD请求方式的响应效率会高些。”也就是说,如果Servlet覆盖了doHead方法,对Head请求则调用子类的doHead方法,否则,调用父类的doHead方法,在父类的doHead方法中则是调用doGet方法,但它仅仅是输出了doGet方法产生的相应头部分,而没有输出doGet方法产生的响应正文部分。如果你是HttpServlet类的设计者,你将通过何种编程方式来实现这种功能的,请从tomcat.apache.org站点下载其源文件后,找到HttpServlet类的源文件,然后描述一下其设计思路。

答:

   protected void doHead(HttpServletRequest req, HttpServletResponse resp)

       throwsServletException, IOException

    {

       NoBodyResponseresponse = new NoBodyResponse(resp);

      

       doGet(req,response);

       response.setContentLength();

    }

 

 

 

 

 

抱歉!评论已关闭.