`

05_HttpServletRequest

 
阅读更多

一、请求转发(forward)和资源包含(include)

 

一、请求转发(forward)和资源包含(include)

1、请求转发(forward)

      forward 方法用于将请求转发到RequestDispatcher 对象封装的资源,Servlet 程序在调用这个方法进行转发之前可以对请求进行一些前期的预处理。在后面要讲解的MVC架构中,forward方法是一个核心方法,控制器组件(Controller)就是使用该方法来跳转到视图组件(Viewer),让视图组件产生响应内容返回给浏览器显示。使用forward方法时,应注意下面一些问题:

     (1)如果在调用forwar方法之前,在Servlet程序中写入的部分内容已经被真正地传送到了客户端,forward方法将抛出IllegalStateException异常。由于IllegalStateException是RuntimeException的子类,属于系统级的异常,所以在程序代码中并不强制要求使用try ... catch 语句进行处理。

     (2)调用RequestDispatcher.forward 方法时,Servlet 容器将根据目标资源路径对当前HttpServletRequest对象中的请求路径和参数信息进行调整。

     (3)如果在调用forward方法之前向Servlet引擎的缓冲区中写入了内容,只要写入到缓冲区中的内容还没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区中的内容将被清空。在调用forward方法之后,如果调用者程序继续向Servlet引擎缓冲区中执行写入操作,这些写入操作的执行结果将被忽略。

       说明:如果forward之前缓冲区的内容已经被输出到客户端,那么就会报第一条的异常。如果还没有真正输出到客户端那么将被清空,并且forward后调用者如果继续向缓冲区写信息也将会被忽略,这是因为forward方法,已经将请求转发出去,可以认为这次的请求完全由被调用者来进行响应,调用者进行的对缓冲区的写入全部认为是无效信息。

     (4)在调用者程序中设置的响应状态码和响应头不会被忽略,在被调用者程序中设置的响应状态码和响应头也不会被忽略。注意:对于RequestDispatcher.include 方法调用,只有调用者程序设置的响应状态码和响应头才会有效,在被调用者程序中设置的响应状态码和响应头将被忽略。

      说明:forward方法的调用者和被调用者可以认为是对等的并且它们共享同一个Request和Response,所以都有权利对Request和Response进行修改,只要符合Servlet本身对Response头信息的修改规则化,无论在哪里设置的响应头和响应码都是有效的。例如:在调用者中设置  response.setContentType("text/html;charset=ISO8859-1");  随后在被调用者中设置 response.setContentType("text/html;charset=GB2312"); 如果这两次设置都是在response.getWriter()之前那么最终响应头编码就会按照最后设置的GB2312来处理。不管是在调用者还是被调用者中如果在response.getWriter()之后进行设置contentType信息都将是无效的,因为即使在同一个Servlet中如果在response.getWriter()之后进行也是无效的。

     (5)如果调用者与被调用者的访问URL不属于同一个目录,当被调用者输出的内容中包含使用相对URL的访问路径时,原来相对被调用者的URL将变成相对于调用者的URL。这是因为浏览器只知道当前访问的是调用者程序的URL,所以对响应内容中的相对路径都以调用者程序的URL作为基准进行计算,这样就导致被调用者程序输出的相对路径不再是相对于被调用者的路径了。

      说明:当在被调用者中使用相对路径的时候Servlet引擎就会将类似 /TestWSServer/ForwardedServlet  的相对路径返回给浏览器,由浏览器负责将相对路径转换为绝对路径,可以对于浏览器来说,它只知道目前请求路径为调用者,所以它就会按照调用者做为基准路径进行转换,这样一来如果我们在被调用者中使用相对路径的话,有两个解决方法,要么使用<base>标签语句指定网页中相对路径的基准地址或者以调用中作为基准路径使用相对路径。

综合上面几条注意事项,可以总结为调用者和被调用者的3条不同点和1条相同点:

不同点 缓冲区操作 当缓冲区的数据真正输出到客户端的时候,如果调用forward方法会报异常;如果没有真正输出到客户端的话,调用者对缓冲区的一切操作都将被忽略,只关注被调用者的输出缓冲区内容。
不同点 forward时Request中的路径和参数 forward的时候Servlet 容器将根据目标资源路径对当前HttpServletRequest对象中的请求路径和参数信息进行调整
不同点 相对路径问题 当在被调用者中使用相对路径的时候,响应会将相对路径原样返回给浏览器,浏览器根据这个相对路径组拼成绝对路径,可以浏览器只知道这次请求的调用者信息,所以会将调用者的路径作为基准路劲组拼绝对路径,这样一来如果调用者和被调用者的路径不在同一目录的话就会出现问题。如上面说明:只能使用<base>标签或将调用者的路径作为基准路径在使用相对路径。
相同点 对响应头和响应码的设置

调用者和被调用者设置的响应码和响应头都不会被忽略。但是必须遵循原本Servelt的规定,例如设置response.setContentType("text/html;charset=XXX"); 的时候虽然调用者和被调用者设置的响应头都不会忽略,最终有效设置为第一次调用response.getWriter()前的最后一次,因为即使是在同一个Servelt中也只有第一次调用

response.getWriter()前的最后一次设置才为最终设置。

 针对上面几条的的一个关于forward跳转的例子程序如下:

public class ForwardingServlet extends HttpServlet {

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//禁止浏览器缓存页面信息
		response.setDateHeader("Expires", 0);
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Pragma", "no-cache");
		
		//设置响应头类型和编码信息
		response.setContentType("text/html;charset=ISO8859-1");
		response.setContentType("text/html;charset=GB2312");		
		PrintWriter out = response.getWriter();		
		response.setContentType("text/html;charset=UTF-8");
		
		//后台打印forward前Request请求信息的URI,查询字符串和URL信息
		System.out.println("URI = " + request.getRequestURI());
		System.out.println("QueryString = " + request.getQueryString());
		System.out.println("URL = " + request.getRequestURL().toString());
				
		String china = "中国";
		//forward前向缓存区写入一些内容
		out.println("调用者:forward之前写入的内容");
		request.getRequestDispatcher("/ForwardedServlet?p1=" + URLEncoder.encode(china, "GBK")).forward(request, response);
		//forward后调用者继续向缓冲区写入一些内容
		out.println("调用者:forward之后写入的内容");
	}
}

 

public class ForwardedServlet extends HttpServlet {

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//向缓冲区写入Request请求中的URI,查询字符串和URL信息,并且以自己为基准的相对路径来访问自己
		PrintWriter out = response.getWriter();
		out.println("<a href='ForwardedServlet' >访问自己<a/><br>");
		out.println("URI:" + request.getRequestURI() + "<br>");
		out.println("QueryString:" + request.getQueryString() + "<br>");
		out.println("URL:" + request.getRequestURL().toString() + "<br>");
		
		String p1 = request.getParameter("p1");
		String chP1 = null;
		if(p1 != null) {
			chP1 = new String(p1.getBytes("ISO8859-1"),"GB2312");
		}
		
		//输出forward时传入的参数内容
		out.println("parameter p1:" + chP1 + "<br>");		
	}

}
	<servlet>
		<servlet-name>ForwardingServlet</servlet-name>
		<servlet-class>com.test.servlet.ForwardingServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>ForwardingServlet</servlet-name>
		<url-pattern>/Servlet/ForwardingServlet</url-pattern>
	</servlet-mapping> 	
	<servlet>
		<servlet-name>ForwardedServlet</servlet-name>
		<servlet-class>com.test.servlet.ForwardedServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>ForwardedServlet</servlet-name>
		<url-pattern>/ForwardedServlet</url-pattern>
	</servlet-mapping> 	

后台输出内容为:

URI = /TestWSServer/Servlet/ForwardingServlet
QueryString = p1=param1
URL = http://localhost:8080/TestWSServer/Servlet/ForwardingServlet

 

页面输出内容为:

访问自己
URI:/TestWSServer/ForwardedServlet
QueryString:p1=%D6%D0%B9%FA
URL:http://localhost:8080/TestWSServer/ForwardedServlet
parameter p1:中国

 

从前后台的输出内容来看,可以根据现象来解释forward过程的几点注意:

 注意事项  现象
 forward转发的时候会对Request中的路径和参数信息进行修改  后台输出的URI,查询参数和URL路径不相同
 当调用者写入缓冲区的内容没有被真正写入客户端之前forward,这些内容将被清空,forward后调用者如果继续向缓冲区写内容也将被忽略(当然通过flush强制写入到客户端后,在forward的情况这里就不模拟了)  客户端没有输出:调用者:forward之前写入的内容 和 调用者:forward之后写入的内容
 调用者和被调用者对响应头和响应码的修改都不会被忽略,不过像  response.setContentType("text/html;charset=XXXX"); 这样的响应头信息,只有在第一次调用response.getWriter();前的最后一次设置才有效

 在调用response.getWriter();后,即使调用了response.setContentType("text/html;charset=UTF-8");
响应信息还是按照调用response.getWriter();前的最后一次设置的GB2312来输出响应内容

 

 被调用者如果使用相对路径的话,不能以自己作为基准,应该用调用者作为基准,因为浏览器在将相对路径转换成绝对路径的时候只知道调用者的路径信息,所以会将调用者作为基准来生成绝对路径的。当然用<Base>标签也能解决这个问题

 

 点击 访问自己 的链接,浏览器地址为

http://localhost:8080/TestWSServer/Servlet/ForwardedServlet   而不是我们期望的

http://localhost:8080/TestWSServer/ForwardedServlet

 

2、资源包含(include)

     RequestDispatcher.include 方法用于将RequestDispatcher 对象封装的资源内容作为当前响应内容的一部分包含进来,从而实现可编程的服务器端包含功能。被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它里面存在这样的语句,这些语句的执行结果将被忽略。在调用RequestDispatcher.include 方法时,Servlet容器不会去调整 HttpServletRequest对象中的信息,HttpServletRequest 对象仍然保持其初始的URL路径和参数信息,也就是说,在被调用者程序中检索当前访问路径时,得到的结果是调用者程序的URL路径,而不是被调用者程序自己的URL路径。

演示代码如下:

public class IncludingServlet extends HttpServlet {

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		response.setDateHeader("Expires", 0);
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Pragma", "no-cache");
		
		response.setContentType("text/html;charset=GB2312");
		PrintWriter out = response.getWriter();
		String china = "中国";		
		RequestDispatcher rd = getServletContext().getRequestDispatcher("/IncludedServlet?p1=" + URLEncoder.encode(china));	
		out.println("before including " + "<br>");
		rd.include(request, response);
		out.println("after including " + "<br>");
	}	
}

 

public class IncludedServlet extends HttpServlet {

	protected void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/html;charset=ISO8859-1");
		PrintWriter out = response.getWriter();	
		out.println("中国" + "<br>");
		out.println("URI =" + request.getRequestURI() + "<br>");
		out.println("QueryString = " + request.getQueryString()	+ "<br>");
		out.println("URL = " + request.getRequestURL().toString() + "<br>");
		out.println("parameter p1 = " + new String(request.getParameter("p1").getBytes("ISO8859-1"),"GB2312") + "<br>");
	}
	
}

 

	<servlet>
		<servlet-name>IncludingServlet</servlet-name>
		<servlet-class>com.servlet.IncludingServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>IncludingServlet</servlet-name>
		<url-pattern>/IncludingServlet</url-pattern>
	</servlet-mapping> 
			<servlet>
		<servlet-name>IncludedServlet</servlet-name>
		<servlet-class>com.servlet.IncludedServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>IncludedServlet</servlet-name>
		<url-pattern>/IncludedServlet</url-pattern>
	</servlet-mapping> 

 

输出结果为:
中国
URI =/TestWSServer/IncludingServlet
QueryString = null
URL = http://localhost:8080/TestWSServer/IncludingServlet
parameter p1 = 中国
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics