Servlet的学习之Request请求对象(3)

  本篇接上一篇,将Servlet中的HttpServletRequest对象获取RequestDispatcher对象后能进行的【转发】forward功能和【包含】include功能介绍完。

  首先来看RequestDispatcher对象的“转发”功能:

  在《Servlet的学习(五)》中说过,使用ServletContext对象的getRequestDispatcher方法可以获得转发对象RequestDispatcher对象,将请求进行转发给其他的Servlet或者JSP处理,同时在该篇的结尾,也注明了其实使用ServletContext的方式不适合实际开发,因为ServletContext对象是web域对象,在web应用部署的整个生命周期内,是作为所有Servlet的共同拥有者,这就导致了在多线程情况下会引起ServletContext对象中用于转发的数据产生线程安全问题:一个Servlet利用ServletContext在转发给JSP过程中,而另一个线程中的Servlet使用ServletContext将这个转发的数据给覆盖,这样导致原先该转发给JSP的数据并不是我们期待的。

  而Request请求对象也是域对象,虽然这个域对象的生命周期并没有ServletContext那么长,但是如果采用Request对象的转发还是能持续到别的Servlet处理时的。使用Request请求对象来处理转发的最大的好处就是在多线程中,每一个线程都有自己的Request请求对象,这就不会像ServletContext对象一样是线程所共有的,因此不会产生线程安全问题。

  那么我们将《Servlet的学习(五)》中使用ServletContext对象来转发的例子改为使用HttpServletRequest请求对象来转发:

  在【myservlet】web工程下创建一个名为“ServletRequest1”的Servlet和一个show.jsp

在ServletRequest1中将数据转发给show.jsp,代码为:

1     public void doGet(HttpServletRequest request, HttpServletResponse response)
2             throws ServletException, IOException {
3
4         String data = "Ding love LRR";
5         request.setAttribute("data", data);
6         RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
7         dispatcher.forward(request, response);
8     }

而在show.jsp中接收这个数据(属性),并封装在HTML中:

1 <font size="100px" color="red">
2         <%
3             String data = (String)request.getAttribute("data");
4             out.write(data);
5         %>
6 </font>

在浏览器中访问这个Servlet,会看到:

  

当然,还是跟之前一样,转发功能不会使浏览器的地址发生变化。

  最后再说一句,转发功能之所以这么重要,主要原因在于在MVC设计模式下,都是Servlet处理用户请求的,并产生用户想看的数据,然后转交给JSP显示,在转交给JSP显示时,会把数据存在Request域对象里带给JSP。

转发功能还有一些要注意的地方:

如果在调用RequestDispatcher对象的forward方法之前,将Servlet程序通过Response响应对象写出数据之后如果关闭流或刷新流,这是将缓存的数据发送给了客户端,相当于向客户端提交了数据,那么再使用Request请求对象来转发将会抛出IllegalStateException异常:

  如我们在上述Servlet中的代码前加入向该Servlet写入数据:

1     PrintWriter writer = response.getWriter();
2     writer.write("Long live sd");
3     writer.close();    //或writer.flush(),这步相当于向客户端提交了响应
4
5     request.setAttribute("data", "Ding love LRR");
6     RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
7     dispatcher.forward(request, response);

那么我们在浏览器将只能访问响应提交的数据,而不能看到转发后的页面:

  

同时服务器后台报错,抛出IllegalStateException异常:

  

从提示也可以看出,在转发之前不能提交Response响应(Cannot forward after response has been committed)。

而如果我们将Response的输出流不关闭,或者放最后关闭,则可以看到转发还是成功的:

1     PrintWriter writer = response.getWriter();
2     writer.write("Long live sd");
3
4     request.setAttribute("data", "Ding love LRR");
5     RequestDispatcher dispatcher = request.getRequestDispatcher("/show.jsp");
6     dispatcher.forward(request, response);
7
8     writer.close();  //response的输出流在转发之后再关闭

可以访问到要转发的页面(show.jsp代码在上面),同时服务器后台也不报错:

  

这是因为通过write方法写入的数据还在Response对象的缓冲区中,我们知道如果使用了close()或flush()都会将缓存的数据立马真正输出,那就相当于已经想客户端提交了响应,之后如果再转发肯定会抛出异常。而如果我们没有输出数据(没有提交响应),那么转发功能正常,而原先通过Response响应对象写入缓冲区中的数据将会被清空,但是通过设置响应头字段信息的内容将保持有效(例如在转发之前写的response.setContentType方法等)。

另外,如果我们在转发之后再继续通过Response对象写出数据,也是没有用的,因为转发就相当于已经将请求和响应提交给客户端了,看到的还是转发的页面,不过这时候服务器后台不会报错就是了。

  总结,在第一次【转发】之前或之后向客户端写出数据或者再次转发都是不合理的。建议在转发语句后直接跟一个return是非常理智的选择。

  另外,如果在转发一次之后再转发还是会发生IllegalStateException异常,因为转发一次就相当于向服务器提交一次Response响应了:

1     request.getRequestDispatcher("/index.jsp").forward(request, response);
2
3     request.getRequestDispatcher("/1.html").forward(request, response);

在浏览器中只能看到最开始转发的那个页面:

  

同时服务器后台抛出IllegalStateException异常:

  

这种问题在使用if条件语句时要特别注意。

==========================================================================

接下来看RequestDispatcher对象的“包含”功能:

  RequestDispatcher对象的include方法用于将一些web资源(如Servlet,JSP,HTML等)包含在Response响应对象中。

  举个例子,在【myservlet】中创建一个【public】文件夹,里面再创建“header.html”和“footer.html”,另外创建一个Servlet

在header.html页面中代码为:

  

在footer.html页面中代码为:

  

而在Servlet中代码为:

1     request.getRequestDispatcher("public/header.html").include(request, response);
2     response.getWriter().write("long live sd  <br />");
3     request.getRequestDispatcher("public/footer.html").include(request, response);

访问该Servlet后发现:

  

原来响应写入HTML是使用OutputStream,与JSP不同()(《Servlet的学习之Response响应对象(3)》)

那么代码只好改为:

1     request.getRequestDispatcher("/public/header.html").include(request, response);
2     response.getOutputStream().write("long live sd <br />".getBytes());
3     request.getRequestDispatcher("/public/footer.html").include(request, response);

那么再次访问该Servlet:

  

注意:被包含的Servlet(如上例中的Servlet)不能改变响应对象中的响应头和响应状态码,如果在包含的Servlet中含有这样企图更改的句子,则会被服务器忽略。

==========================================================================

最后来总结下RequestDispatcher的forward转发和Response的sendRedirect重定向的区别:

  1,forward转发只能将请求转发给该web应用中的web资源。

    sendRedirect重定向除了能重定向到该web应用中的资源外,还能重定向到同一个主机(或服务器)上的其他web应用中的资源,甚至可以使用绝对URL重定向到其他主机(或服务器)上得资源。

  2,创建RequestDispatcher对象时指定的相对URL以“/”开头,相当于当前web应用程序的根目录,也就是说在该“/”之后代表当前web应用所在目录下的内容,例如:

request.getRequestDispatcher("/index.jsp")

    如果是调用HttpServletResponse.sendRedirect方法的相对URL以“/”开头,则是相对整个服务器存放web应用的根目录,例如以Tomcat的【webapps】目录为重定向目录的根目录,如:response.sendRedirect(“/myservlet/index.jsp”),其中”myservlet”是web工程名,同时也是web应用所在目录名,存放于Tomcat的【webapps】目录下。

  3,调用RequestDispatcher对象的转发forward方法进行转发过程结束后,浏览器地址栏中的URL地址不会发生改变。

    而调用HttpServletResponse.sendRedirect重定向方法则在访问过程结束后,浏览器地址栏中的URL地址将会变成重定向资源的路径。

  4,RequestDispatcher对象的转发forward方法的调用者和被调用者共享相同的request请求对象和response响应对象,它们属于同一个访问请求和响应过程。

    而HttpServletResponse.sendRedirect重定向方法的调用者和被调用者使用各自的request请求对象和response响应对象,它们属于两个独立的访问请求和响应过程。

有一个例子可以比较形象的说明转发和重定向的不同:

  转发:A向B借钱,B说没有,但是B帮A向C借钱。

  重定向:A向B借钱,B说没有,B让A重新向C借钱。

时间: 2024-10-12 17:47:02

Servlet的学习之Request请求对象(3)的相关文章

Drf04 / drf request请求对象封装、版本、认证、权限

目录 Drf04 / drf request请求对象封装.版本.认证.权限 回顾和补充 今日详细 1.请求的封装 2.版本 3.认证(面试) 4.权限 Drf04 / drf request请求对象封装.版本.认证.权限 回顾和补充 restful规范 1. 建议用https代替http 2. 在URL中体现api,添加api标识 https://www.cnblogs.com/xwgblog/p/11812244.html # 错误 https://www.cnblogs.com/api/xw

django 获取request请求对象及response响应对象中的各种属性值

1 django request对象和HttpResponse对象 2 HttpRequest对象(除非特殊说明,所有属性都是只读,session属性是个例外) 3 HttpRequest.scheme 请求方案(通常为http或https) 4 HttpRequest.body 字节字符串,表示原始http请求正文 5 HttpRequest.path 字符串,表示请求的页面的完整路径,不包含域名 6 7 HttpRequest.get_host() 获取主机地址 8 9 HttpReques

Request To JavaBean(请求对象转换为JavaBean对象)

一 参考资料 1 http://jc-dreaming.iteye.com/blog/563893 2 http://www.iteye.com/topic/76043 3 http://xubindehao.iteye.com/blog/754807 4 http://javafenger.iteye.com/blog/96829 5 http://www.kaila.com.cn/space.php?uid=7114&do=blog&id=458578 6 http://blog.cs

Django视图函数之request请求与response响应对象

官方文档: https://docs.djangoproject.com/en/1.11/ref/request-response/ 视图中的request请求对象: 当请求页面时,Django创建一个HttpRequest包含有关请求的元数据的对象. 常用的值和操作: ·         request.method      #获取请求属性 ·         request.GET         #获取GET请求的参数字典信息,用.get()取值 ·         request.PO

Servlet的学习(十一)

在上一篇<Servlet的学习(十)>中介绍了HttpServletRequest请求对象的一些常用方法,而从这篇起开始介绍和学习HttpServletRequest的常用功能. 使用HttpServletRequest可以防止盗链行为,什么是盗链行为,比如说在一个别的网站上超链接,指向我们的网页中的某个数据,这样从他的网页上就可以直接进入到我的某个页面,无需从我的指定路口进入: 例如在一个简单的1.html文件中加入了我的[myservlet]web应用下的某个Servlet访问的超链接:

Servlet的学习(九)

本篇来说明响应对象HttpServletResponse对象的最后一点内容. 首先来看响应对象控制浏览器定时刷新,在我的web应用[myservlet]中创建Servlet,在该Servlet中设置响应头,定时刷新的代码很简单: response.setHeader("refresh", "3 "); //3秒刷新一次 就可告知浏览器3秒刷新一次网页.当然“Refresh”响应头还是可以定时跳转到指定页面,如下代码: response.setHeader("

Servlet的学习之Cookie

从本篇开始学习Servlet技术中的Cookie专题. 首先来了解什么是“会话”.会话是web技术中的一个术语,可以简单的理解为:用户打开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,这个过程称为一个会话. 如果在打开一个浏览器访问一个页面后,再打开一个浏览器访问同一个页面,那这就是有两个会话:而打开一个浏览器访问一个页面后,通过这个页面上的某个超链接是从新的浏览器打开的,那依然只算一个会话. 每个用户在使用浏览器与服务器进行会话的过程中,各自不可避免地会产生一些数据,而

ThinkPHP5学习笔记(6)请求和响应

在线视频课程:http://www.kancloud.cn/tpshop/thinkphp5/220692 Request请求对象和Response响应对象 $_GET.$_POST.$_REQUEST.$_COOKIE 调用请求对象$request=Request::instance() $this->request或request()->url();助手函数 input()助手函数 响应格式 json xml 模板渲染 页面跳转 重定向 $this->success $this-&g

重温Servlet学习笔记--request对象

request和response是一对搭档,一个负责请求一个负责响应,都是Servlet.service()方法的参数,response的知识点前面梳理过了,这里只说一下request,在客户端发出每个请求时,服务器都会创建一个request对象,把请求的数据封装到request中,然后在调用Servlet.service()中传递进去.我们在创建一个servlet时通常会使用doGet或者doPost方法,并没有看到service()方法,就是因为父类的service方法已经读取到了reque