`
喻红叶
  • 浏览: 39476 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

Servlet中的重定向

 
阅读更多

Servlet规范定义了一个接口用于请求转发,即RequestDispatcher,该接口有Servlet引擎提供实现,它从客户端接受请求并把该请求转发到容器的任何资源上(包括Servlet,JSP,,HTML等)。虽然它可以包装任何资源,但是Servlet规范建议尽量只用来包装Servlet。RequestDispatcher接口规定了两个方法:

void forward(ServletRequestrequest,ServletResponseresponse)
将一个servlet的请求转发到其他资源(servlet,JSP,HTML)
void include(ServletRequestrequest,ServletResponseresponse)
在同一个响应中,将其他资源包含进来
有一点必须强调:一个RequestDispatcher只能转发或者包含同一个Web应用中的资源。以Tomcat的容器模型为例(图是许令波画的,在此借用一下,希望不要介意):

真正管理Servlet的是Context容器,一个Context容器对应着一个Web应用。前面说了,RequestDispatcher是有Servlet容器创建的,那么这个RequestDispatcher对象只能在创建它的Context返回内实现转发。需要与这种情况区分开来:比如上图有两个Context,假设现在在左边的Context,然后获得了右边Context的ServletContext对象contextR,通过调用contextR.getRequestDispatcher()获得RequestDispatcher对象,重定向到右边的Context,貌似使用RequestDispatcher从左边的Context重定向到了右边的Cotnext上了。但是请注意,这个RequestDispatcher对象是右边的Context创建的,它能且只能转发到此Context,并不是左边的Context创建的RequestDispatcher重定向到了右边的Context上。

获得RequestDispatcher

RequestDispatcher是容器创建的,程序员无法创建,ServletContext和ServletRequest中都定义了获得RequestDispatcher的方法,先来看ServletContext接口中定义的两个用于获取的方法:

/**
 * 返回包装了某个路径所指定的资源的RequestDispatcher对象
 * 传递给该方法的路径必须以“/”开头,"/"代表当前Web应用的根
 * 目录,WEB-INF中的内容对RequestDispatcher对象可见
 */
RequestDispatcher getRequestDispatcher(java.lang.String path);
/**
 * 返回包装了某个Servlet或者JSP的RequestDispatcher对象
 * 传递给该方法的参数是在Web应用中指定的Servlet或JSP的名称
 * 可以通过调用ServletConfig.getSerlvetName()确定
 */
RequestDispatcher getNamedDispatcher(java.lang.String name)
注意,getNamedDispatcher()方法只能获得Serlvet或JSP的包装对象。ServletRequest接口中也提供了获取的方法:
/**
 * 与ServletContext中的getRequestDispatcher(String)的区别:
 * 可以使用"/","/"代表当前Web应用的根目录外,这个和前面相同
 * 出了使用"/"开头的外,还可以使用不以"/"开头的的相对路径
 */
RequestDispatcher getRequestDispatcher(java.lang.String path);

有了RequestDispatcher对象,那就来看看它可以做写什么吧。

include实现资源包含

include方法用于将RequestDispatcher对象封装的资源内容作为当前相应的一部分包含进来。其实就是相当于组装,比如一个页面包含几个部分,每个部分放到不同的页面去实现,然后使用include方法将几个页面整合到到一个页面里,在调用include方法之前和之后的输出都是有效的。它们共享同一个request和response,不过被包含的页面不能改变响应消息的状态码和状态头,如果它存在这样的语句,这也设置将被忽略。下图展示了include的结构:


下面给出一个示例:

/**
 * 包含文件和被包含文件是同一个文件,请求和处理都是相同的
 * 只不过在被包含文件中设置响应消息的状态吗和响应头全部是小
 */
@WebServlet("/dispatch/Including")
public class Including extends HttpServlet {
	private static final long serialVersionUID = 1L;
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		String value = "我在冰封的深海找寻希望的缺口,却在午夜惊醒时,蓦然瞥见绝美的月光";
		
		//给request添加属性,看看被包含的页面能否获得此属性
		request.setAttribute("content", value);
		out.println("-------------------------<br/>");
		//获得被包装资源的RequestDispatcher对象
		RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/dispatch/Included");
		//包含进来
		dispatch.include(request, response);
		out.println("<br/>----------end------------");
	}
}
---------------------------------------------------------------
/**
 * 被包含的页面
 */
@WebServlet("/dispatch/Included")
public class Included extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//这里的设置将被忽略
		response.setCharacterEncoding("UTF-8");
		
		PrintWriter out = response.getWriter();
		//获得request中的属性
		out.println(request.getAttribute("content"));
		/*这里的PrintWriter和包含页面中的PrintWriter是同一个对象
		 * 如果在这里把输出流关闭了,则包含页面中位于include方法的
		 * 内容将不会被输出了
		 */
		//out.close();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request,response);
	}
}
在包含页面并没有设置编码方式,但是在被包含页面中设置了使用UTF-8编码,但是输出结果却是这样的:
-------------------------
???????????????????????????????? 
--------------end----------
这样说了在被包含页面中设置的响应头被忽略了。

forward实现请求转发

forward方法用于将一个请求转发到RequestDispatcher对象封装的资源,Servlet程序在调用这个方法进行转发之前可以对请求进行一些前期预处理,在调用forward方法时需要注意一下几点:

(1)在调用forward方法之前,实现转发的Serlvet不能有内容输出到客户端。如果在调用forward之前向Servlet引擎缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法执行后,原来被输出到缓冲区中的内容将被清空;如果之前写入缓冲区的内容已经输出到客户端,那么调用forward时会抛出IllegalStateException异常;

(2)在调用forward方法之后,实现转发的Servlet会继续执行,直到它的逻辑完成,但是它的任何向客户端的输出都将被忽略;

(3)在调用者和被调用者程序中设置的响应状态吗和响应头都有效;

(4)如果调用者与被调用者访问的URL不属于同一目录,当被调用这输出到内容中包含使用相对URL的访问路径时,原来相对被调用者的URL将变成相对于调用者的URL。这是因为浏览器只直到当前访问的是调用者的URL,所以浏览器都已调用者作为参考,后面会使用图解释这种请求过程。

下面是一个示例,演示会演示上面提到的其中情况:

/**
 * 调用者
 */
@WebServlet("/dispatch/ForwardDemo")
public class ForwardDemo extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();
		//在forward方法调用之前不能有内容输出到客户端
		//如果在缓冲区中,则会被忽略
		out.println("在调用forward方法之前");
		//如果将缓冲区的内容强制输出到客户端,则会抛异常
		//response.flushBuffer();
		
		//设置属性,证明在被调用者中能获取到
		request.setAttribute("content", "那时我们有梦");
		
		RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/Forwarded");
		dispatch.forward(request, response);
		
		//在forward方法之后的输出都将被忽略
		out.println("在forward方法之后");
		
		//但是调用者的逻辑也会正常执行,直到结束
		System.out.println("虽然在forward之后,还是得到了执行");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request,response);
	}

}
-----------------------------------------------------------
/**
 * 被调用者程序
 */
@WebServlet("/Forwarded")
public class Forwarded extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		
		//获得在调用者程序中设置的属性
		out.println(request.getAttribute("content") + "<br/>");
		
		out.println("关于文学<br>关于爱情<br/>关于穿越世界的旅行<br/>"
				+ "如今我们深夜饮酒<br/>杯子碰到一起都是梦破碎的声音<br/>");
		
		RequestDispatcher dispatch = getServletContext().getRequestDispatcher("/html/dispatch.html");
		dispatch.forward(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request,response);
	}

}
在上面的实例中,其实被调用者也充当了调用者,所以它的内容实际上也不会被输出。注意关于相对路径的问题,现在在html路径下有两个文件:dispatch.html和Refresh.html,如果要在dispatch.html中使用链接指向Refresh.html,由于它们在同一个目录下那么应该这样写:

<a href="Refresh.html">刷新</a>

但是经过forward转发后,相对路径就不是dispatch.html所在的目录,而是以ForwardDemo(第一个调用者)的路径为相当目录了,如果想上面这样调用的话,浏览器会认为它访问的是这个路径:

http://localhost:8080/ServletJSP/dispatch/Refresh.html

所以应该这么调用:

<a href="../html/Refresh.html">必须以调用者的目录为相对路径</a>

运行上面的示例后,查看后台会发现有一条输出语句:“虽然在forward之后,还是得到了执行”,这说明调用者的逻辑还是会被正常执行。

HttpServletResponse.sendRedirect

sendRedirect(String url)也可以实现重定向,它会生成302状态码和Location相应头,通知客户端去重新访问Location响应头中指定的URL,可以是相对的URL也可以是绝对的URL,如果URL以"/"开头,则代表整个Web站点的根目录,不是Web应用的根目录,比如以Tomcat为例,"/"在这里表示:http://localhost:8080;如果不以“/”开头,则表示相对于当前请求的URL。sendRedirect与forward不同时,它可以重定向到其他站点。示例代码如下,跳转到Google主页:

/**
 * ServletResponse.sendRedirect
 */
@WebServlet("/dispatch/RedirectServlet")
public class RedirectServlet extends HttpServlet {
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		//同样不能有输出,否则抛异常
		out.println("before sendRedirect");
		//response.flushBuffer();
		
		//如果是Web应用中跳转,需要加上Web应用的路径
		//response.sendRedirect(getServletContext().getContextPath() + "/html/Refresh.html");
		//可以定位到其他站点
		response.sendRedirect("http://www.google.com");
		
		//后面的输出会被忽略,但是逻辑会完成
		out.println("after sendRedirect");
		System.out.println("after sendRedirect");
	}
}
区别

RequestDispatcher.forward(resquest,response)和ServletRequest.sendRedirect(url)的区别:

(1)sendRedirect可以重定向到其他站点;

(2)最重要的区别是,他们的运行原理完全,首先看forward的运行过程:

(1)客户端发送请求,处理请求的是页面1;

(2)页面1发现需要跳转到页面2,于是在服务器端直接转发到页面2;

(3)页面2响应请求;

在整个过程中,浏览器地址栏不会发生改变,页面1和页面2是同一个请求和响应,也就是说reqeust和response是同一个。以上的过程如下图所示:


而sendRedirect的请求过程则是这样的:

(1)浏览器请求页面1;

(2)服务器发现需要转发到页面2,于是给浏览器发送302状态码,并然浏览器去访问Location头中的URL;

(3)浏览器请求页面2;

(4)页面2做出响应;

在上面的过程中,浏览器发生了两次请求,所以地址栏中的地址会发生改变,两次请求不会共享request,response,服务器会生成两对request和response,以上过程如下图所示:

以上介绍了Servlet中的重定向,JSP中有一套几乎相同的机制,其实JSP本身就是Servlet嘛。

转载请注明出处:喻红叶《Servlet中的重定向》

分享到:
评论

相关推荐

    JSPServlet的重定向技术综述

    JSPServlet的重定向技术综述 详细介绍

    servlet使用及重定向.rar

    使用myEclipse + tomcat Servlet的简单例子,包括重定向,Session的简单使用 初学者使用......高手勿入 www.zhuyi123.cn助益信息网经常有新的信息 助益网

    servlet请求转发、重定向、包含

    servlet请求转发、请求重定向、请求包含 获取form数据

    浅谈Servlet 实现网页重定向的方法

    本篇文章主要介绍了Servlet 实现重定向几种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    java servlet请求转发重定向

    java servlet请求转发重定向 适合于初学者更好的了解页面跳转和原理

    Servlet转发与重定向

    通过网上总结的Servlet的转发与重定向的区别介绍。希望可以帮的上大家

    servlet重定向详解(八)

    主要为大家详细介绍了servlet重定向的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    jsp跳转的五种方式

     例:在servlet中重定向 public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException {  response.setContentType("text/html; charset=gb2312");  ...

    javaServlet请求转发和重定向.pdf

    javaServlet请求转发和重定向.pdf

    JSP-Servlet.rar_jsp_jsp综述_servlet_servlet jsp

    这个是有关JSP-Servlet的重定向技术综述,希望对学习jsp和servlet的人有一些帮助!

    USerLogin servlet 登录实例探究转发与重定向本质区别

    USerLogin servlet 登录实例探究转发与重定向本质区别

    3.快捷的重定向方法

    所谓的重定向是将请求重新定个方向转到其他位置。例如,客户端访问AServlet,然后立刻自动访问BServlet。这个过程其实就是重定向。下面通过一张图来了解重定向,如图1-1所示:

    jsp实现用户登录javaweb

    jsp+servlet实现用户登录,可以了解到 jsp以及 servlet的重定向,以及servlet之间的重定向。

    JSP和Servlet面试题

    Servlet的执行流程也就是servlet的生命周期,当服务器启动的时候生命周期开始,然后通过init()《启动顺序根据web.xml里的startup-on-load来确定加载顺序》 方法初始化servlet,再根据不同请求调用doGet或doPost...

    JavaWeb中的 请求转发 和 重定向.docx

    请求转发:在最终的 servlet (TestServlet) 中,request和中转的那个servlet(ForwardServlet...重定向:在最终的 servlet (TestServlet) 中,request和中转的那个servlet(SendServlet)中的request对象不是同一个对象

    在Jsp Servlet中页面重新定向总汇

    是在服务器端起作用, 当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的...

    XML Oracle Servlet 复习汇总(S2)

    7 Servlet:转发和重定向 8分 8 Servlet:连接池 5分 9 Servlet:servlet(代码题目) 6分 10 Servlet:jspuserbean 5分 11 Servlet:servlet(代码题目) 10分 12 Servlet:jstl标签(代码题目) 8分 13 Servlet:...

    servlet实现简单登录验证

    servlet实现简单登录验证,最简单的实现,通过equals()方法然后重定向,主要是理解servlet的运行机制

    servlet+jsp练手小项目

    servlet+jsp练手小项目,适合初学者进行项目的练手,可以帮助我们熟悉servlet+jsp的项目流程,掌握转发,重定向,路径等问题,有助于后面框架的学习,而且项目并不复杂,主要是锻炼大家的思路,让我们可以熟悉servlet...

    JSP与Servlet 技术总结

    3. Servlet 在web.xml中的配置 10 4. Servlet元素说明 11 5. 如何写一个Servlet? 11 6. 在web.xml里配置的初始化参数怎么才能读回来? 12 7. 如何获得网页中form表单中的参数 12 8. session跨请求(页面)传数据怎么...

Global site tag (gtag.js) - Google Analytics