`
喻红叶
  • 浏览: 41071 次
  • 性别: 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的重定向技术是Web开发中两个关键的方法,用于改变HTTP请求的处理路径,以便将用户导向不同的页面或资源。这两种技术分别是`RequestDispatcher.forward()`和`response.sendRedirect()`,它们各有特点和...

    servlet使用及重定向.rar

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

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

    而在重定向中,如果需要保留表单数据,通常需要将它们附加到新URL上或者通过session存储。 在实际应用中,根据业务需求选择合适的方法:如果需要在不改变URL的情况下整合多个资源或传递请求参数,可以使用请求转发...

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

    Servlet 是 Java Web 开发中的一种重要组件,今天我们将讨论如何使用 Servlet 实现网页重定向。 首先, lets 先来了解为什么我们需要网页重定向。网页重定向有很多种情况,例如,当用户访问一个不存在的页面时,...

    Servlet转发与重定向

    Servlet 转发与重定向是 Java Servlet 编程中两个重要的概念,它们都可以让浏览器获得另外一个 URL 所指向的资源,但是它们的内部运行机制有着很大的区别。 RequestDispatcher.forward 方法 RequestDispatcher....

    java servlet请求转发重定向

    ### Java Servlet 请求转发与重定向详解 #### 一、引言 在Java Web开发中,请求转发和重定向是两种非常重要的技术手段,用于控制页面之间的跳转逻辑。这两种技术虽然都能实现页面间的跳转,但其工作原理、应用场景...

    jsp_servlet课程设计作业

    学生需要了解Servlet生命周期,包括加载、初始化、服务、销毁等阶段,以及如何在Servlet中重定向和转发请求。 3. **API接口使用**:项目中提到获取团购网的API,这意味着需要理解RESTful API的工作原理,学习如何...

    servlet+tomcat面试题及答案解析.rar

    6. **如何在Servlet中重定向和转发请求?** 重定向使用`response.sendRedirect()`方法,会发送一个新的HTTP请求到客户端;转发使用`request.getRequestDispatcher()`.forward(request, response)`方法,它在服务器...

    JSP-Servlet.rar_jsp_jsp综述_servlet_servlet jsp

    2. **Servlet中的重定向**:在Servlet中,同样使用`HttpServletResponse`对象的`sendRedirect()`方法: ```java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ...

    servlet重定向详解(八)

    在上面的案例中,Check1Servlet 将客户端的请求重定向到 Out1Servlet 上,并将参数 mess 传递给 Out1Servlet。Out1Servlet 将接收到客户端的请求,并将参数 mess 显示出来。 Servlet 重定向是一种重要的技术,用于...

    Java知识面试综合大全

    面试中可能涉及的问题有“解释JSP的生命周期”、“如何在Servlet中重定向和转发请求?”等。 4. **JavaEE框架知识**:这部分内容可能涵盖Spring、Hibernate、MyBatis等常见的Java企业级框架。Spring框架提供了依赖...

    jsp上传文件教程

    一旦文件成功上传,你需要在JSP页面上显示上传成功的消息,或者在Servlet中重定向到另一个页面。 7. **文件存储策略** 文件可以存储在本地文件系统、云存储服务(如AWS S3、阿里云OSS)或其他持久化存储中。选择...

    JSP期末考试试题及答案

    Servlet中重定向方法 - **题目描述**:Servlet中,哪个方法用来把一个HTTP请求重定向到另外的URL? - **正确答案**:`c) sendRedirect()` - **解析**:`sendRedirect()`方法是`HttpServletResponse`接口中提供的...

    JAVA WEB 基础考试题.docx

    - 解析:选项d“源组件和目标组件共享ServletRequest对象”描述错误,因为在Servlet中进行重定向时,原请求对象并不会传递到新的目标组件,因此正确答案为d。 7. **JSP指令不包括(c)** - 解析:JSP指令包括`...

    javaServlet请求转发和重定向.pdf

    Java Servlet 请求转发和重定向是两种不同的页面跳转机制,它们在 Web 应用程序中扮演着重要的角色。理解这两种机制的区别和使用场景是非常重要的。 请求转发是指将请求再转发到另一资源(一般为 JSP 或 Servlet)...

    servlet 中文帮助文档

    以下是对Servlet中文帮助文档中关键知识点的详细说明: 1. **Servlet生命周期**: - **初始化**:Servlet在第一次请求时被加载,调用`init()`方法初始化。 - **服务**:每当有新的请求到达,Servlet容器(如...

    servlet中文帮助文档CHM格式

    Servlet是Java Web开发中的核心组件,它用于扩展服务器的功能,处理来自客户端(通常是Web浏览器)的请求,并向客户端发送响应。这个"Servlet中文帮助文档CHM格式"提供了一个全面的指南,帮助开发者理解和掌握...

    servlet中实现请求转发,用户登录

    在Servlet中,请求转发与重定向(Redirect)不同。重定向是客户端行为,浏览器收到服务器的302响应后会发起一个新的请求,而请求转发是服务器内部操作,不会生成新的请求。因此,请求转发适用于需要共享请求属性或者...

Global site tag (gtag.js) - Google Analytics