Request介绍及示例 PART1

Request在ServletAPI的规范连接地址http://blog.csdn.net/zghwaicsdn/article/details/51035146

HTTP简单介绍

URL是浏览器寻找信息时所需要的资源位置
。通过URL,人类和应用程序才能找到、使用并共享因特网上大量的数据资源。
URL语法通过格式:
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
几乎没有那个URL中包含了所有这些组件。URL最重要的3个部分是方案(scheme)、主机(host)和路径(path).
常用介绍
方案 scheme:访问服务器以获取资源时要使用那种协议
主机 host:资源宿主服务器的主机名或点分IP地址
端口 prot:资源宿主服务器正在监听的端口号。很多方案都有默认端口号(HTTP的默认端口号为80)
路径 path:服务器上资源的本定名,有一个斜杠将其与前面的URL组件分隔开来。路径组件的语法是与服务器和方案有关的。
查询 query:某些方案会用这个组件传递参数以激活应用程序(如数据库、公告板、搜索引擎以及其他互联网网关),查询组件的内容没有通用格式。
用字符?将其与URL的器与部分分隔开来。

片段 frag:一小片或一部分资源的名字,引用对象时,不会将frag字段传递给服务器,这个字段是在客户端内部使用的。通过字符“#”将其与URL的器与部分分隔开来。

1.方案--使用什么协议
方案实际上是规定如何访问制定资源的主要标识符,它会告诉负责解析URL的应用程序应该使用什么协议。
方案组件必须以一个字母符号开始,有第一个“:”符号将其与URL的其余部分分隔开来。方案名是大小写无关的。
2.主机与端口号
要想在因特网上找到资源,应用程序要知道是那台即其装载了资源,以及在那台机器的什么地方可以找到能对目标资源进行访问的服务器。
主机组件表示了因特网上能够访问资源的宿主机器。可以使用主机名或IP地址来表示主机名。
端口组件标识了服务器正在监听的网络端口。对下层使用TCP协议的Http来说,默认端口号为80。
3.路径
URL的路径组件说明了资源位于服务器的什么地方。路径通常很像一个分级的文件系统路径。
4.查询字符串
通过提问题或者查询来缩小所请求资源类型的范围。
查询字符串以一系列“名/值”对的形式出现,名值对之间用字符“&”分隔

http报文
http是因特网的信使,那么HTTP报文就是它用来搬东西的包裹。
HTTP报文是在HTTP应用程序之间发送的数据块。这些数据块以一些文本形式元信息开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。

这些报文在客户端、服务器和代理之间流动。
HTTP使用流入和流出来描述是无处理的方向。

报文向下游流动
HTTP报文会像河水一样流动,不管是请求报文还是响应报文,所有报文都会向下游流动。所有报文的发送者都在接受者的上游。

报文的组成部分
HTTP报文是简单的格式化数据块。
每条报文都包含一条来自客户端的请求,或者一条来自服务器的响应。

三部分组成:
1.对报文进行描述的起始行(start line)
2.包含属性的首部块(header)
3.可选的、包含数据的主题部分(body)

由一个回车符和一个换行符分隔

请求报文的格式
<method> <request-URL> <version>
<headers>

<entiry-body>

响应报文的格式
<version> <status> <reason-phrase>
<headers>

<entiry-body>
简要描述:
方法(method)
  客户端希望服务器对资源执行的动作。是一个单独的词,比如GET、HEAD或POST。
请求URL(request-URL)
  命名了所请求资源,或者URL路径组件的完整URL。如果直接与服务器进行对化,只要URL的路径组件是资源的绝对路径,
  通常就不会有什么问题--服务器可以假定自己是URL的主机/端口
版本(version)
  报文所使用的HTTP版本,其格式看起来是这样的:
  HTTP/<major>.<minor>
  其中主要版本号(major)和次要版本号(minor)都是整数。
状态码(status-code)
  这三位数字描述了请求过程中所发生的情况。每个请求状态码的第一位数字都用于描述状态的一般类别。
原因短语(reason-phrase)
  数字状态码的可读版本,包含行终止序列之前的所有文本。
首部(header)
  可以有零个或多个首部,每个首部都包含一个名字,后面跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后时一个CRLF。
  首部是由一个空行(CRLF)结束的,表示了首部列表的结束和实体主题部分的开始。
实体的主题部分(entity-body)
  实体的主题部分包含一个由任意数据组成的数据块。并不是所有的报文都包含实体的主题部分,有时,报文只是一个CRLF结束。

起始行
所有的HTTP报文都以一个起始行作为开始。请求报文的起始行说明了要做些什么。响应报文的起始行说明发生了什么。
  请求行
    请求报文请求服务器对资源进行一些操作。请求报文的起始行,或称为请求行,包含了一个方法和一个请求URL,这个方法描述了服务器应该执行的操作,
    请求URL描述了要对那个资源执行这个方法。请求行中还包含HTTP的版本,用来告知服务器,客户端使用的是哪种HTTP。


请求首部
   请求首部只在请求报文中有意义的首部。用于说明是谁或什么在发送请求、请求源来自何处,或者客户端的喜好及能力。服务器可以根据请求首部给出的客户端信息。
试着为客户端提供更好的响应。

Request请求参数的编码问题

1.POST方法请求编码处理


  如果客户端没有在Content-type标头中设置字符编码信息(例如浏览器可以设置Content-Type:text/html;charset=UTF-8),此时使用HttpServletRequest的
  getCharachterEncoding()返回值是null。在这个情况下,容器若使用的默认编码处理是ISO-8859-1,而客户端使用UTF-8发送非ASCII字符的请求参数,Servlet直接
  使用getParameter()等方法取得该请求参数值,就会是不正确的结果也就是得到乱码。
  可以使用HttpServletRequest的setCharacterEncoding()方法指定取得POST请求参数时使用的编码。
  request.setCharacterEncoding("UTF-8");
  //解码
  相当于要求容器作这个操作:String text = java.net.URLDecoder.decode("要转码的参数","UTF-8");
  //编码
  String text = java.net.URLDecoder.decode("原始字符","ISO-8859-1");
一定要在取得任何请求参数前执行request.setCharacterEncoding("UTF-8")方法才有作用。

2.GET请求参数编码处理


setCharacterEncoding()方法只对body中字符编码才有作用,也就是基本上这个方法值对POST产生作用,当请求是GET发送时,则没有定义这个方法是否会影响容器处理
编码的方式(就其原因,是因为处理URL的是HTTP服务器,而非Web容器)。
在Tomcat在GET时,使用setCharacterEncoding()方法设置编码就不会有作用,取得请求参数时仍会产生乱码。
原因是post请求和get请求存放参数位置是不同的:

post方式参数存放在请求数据包的消息体中。get方式参数存放在请求数据包的请求行的URI字段中,以?开始以param=value&parame2=value2的形式附加在URI字段之后。
而request.setCharacterEncoding(charset); 只对消息体中的数据起作用,对于URI字段中的参数不起作用,我们通常通过下面的代码来完成编码转换
   
    String paramValue = request.getParameter("paramName");  
    paramValue = new String(paramValue.trim().getBytes("ISO-8859-1"), charset);  

对于全局请求不管GET或者POST都需要在filter过滤器中doFilter方法中设置,比如:这里并不严格
    request.setCharacterEncoding("UTF-8");
        if(request.getMethod().equals("GET")){
            //设置GET方法参数编码为UTF-8,通过装饰类MyServletRequestWrapper来改变request对象的请求参数编码
            request=new MyServletRequestWrapper(request);
        }

servlet中的请求转发
        
servlet中的请求转发主要有三种方式:
1、  forward:是指转发,将当前request和response对象保存,交给指定的url处理。并没有表示页面的跳转,所以地址栏的地址不会发生改变。

2、  redirect:是指重定向,包含两次浏览器请求,浏览器根据url请求一个新的页面,所有的业务处理都转到下一个页面,地址栏的地址会变发生改变。

3、  include:意为包含,即包含url中的内容,进一步理解为,将url中的内容包含进当前的servlet当中来,并用当前servlet的request和respose来执行
url中的内容处理业务.所以不会发生页面的跳转,地址栏地址不会发生改变。

redirect与include、forward的区别在于是不是同一个Request,redirect会有两次交互。
include与forward的区别在于输出的内容,include包含本身servlet与跳转页面内容的结果,而forward不包含本身servlet的内容。


servlet请求转发与重定向的区别:
request.setAttribute("test","hello");
request.getRequestDispacther("/test.jsp").forword(request,response);
response.sendRedirect("test.jsp");
一、显示结果:
1、当用request.getRequestDispacther("/test.jsp").forword(request,response); 请求转发后,结果页面输出:hello
2、当用response.sendRedirect("test.jsp");重定向后,结果页面输出:null
二、底层分析:
1、请求转发(RequestDispatcher)的过程:
         客户首先发送一个请求到服务器端,服务器端发现匹配的servlet,并指定它去执行,当这个servlet执行完之后,它要调用getRequestDispacther()方法,
         把请求转发给指定的test.jsp,整个流程都是在服务器端完成的,而且是在同一个请求里面完成的,因此servlet和jsp共享的是同一个request,在servlet里面放的所有东西,在jsp中都能取出来,因此,jsp能把结果getAttribute()出来,getAttribute()出来后执行完把结果返回给客户端。整个过程是一个请求,一个响应。
2、重定向(sendRedirect)的工作原理:
     客户发送一个请求到服务器,服务器匹配servlet,这都和请求转发一样,servlet处理完之后调用了sendRedirect()这个方法,这个方法是response的方法,所以,当这个servlet处理完之后,看到response.senRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问test.jsp,
     紧接着客户端受到这个请求后,立刻发出一个新的请求,去请求test.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。
     
     这个方法会在响应中设置HTTP状态码301以及Location标头,浏览器接受到这个标头,会重新使用GET方法请求指定的URL,因此地址栏上会反线URL的变更。
三、表面分析:
    1、当用RequestDispatcher请求转发后,地址栏为http://localhost:8080/test/TestServlet
这真好应正了上面的分析,我们起初请求的就一个servlet,至于你服务器端怎么转,流程怎么样的,我客户端根本就不知道,我发了请求后我就等
着响应,那你服务器那边愿意怎么转就怎么转,我客户端不关心也没法知道,所以当服务器端转发到jsp后,它把结果返回给客户端,客户端根本就
不知道你这个结果是我真正访问的servlet产生的,还是由servlet转发后下一个组件产生的。
    2、当用sendRedirect重定向后,地址栏为http://localhost:8080/test/test.jsp
因为这个时候,客户端已经知道了他第二次请求的是test.jsp,服务器已经告诉客户端要去访问test.jsp了,所以地址栏里会显示想要访问的结果。

如果在处理请求的过程中发现一些错误,而你想要传送服务器默认的状态与错误信息,可以使用sendError()方法。
由于利用到HTTP状态码,要求浏览器重定向网页,因此,sendError()方法同样必须在未确认输出前执行,否则会抛出IllegalStateException

response.sendError(404);

package com.zghw.servlet.demo;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class MyRequestServlet
 */
@WebServlet("/myRequestServlet/*")
public class MyRequestServlet extends HttpServlet {
	static void f(Object obj) {
		System.out.println(obj);
	}
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// http://localhost:8080/servlet-demo/myRequestServlet/test?id=123&abc=222&abc=333
		// URL信息
		Request_URL(request);
		// request请求行
		RequestLine(request);
		// request请求header
		RequestHeader(request);
		// 客户端和服务器端信息地址
		RequestLocalRemoteAddr(request);
		// 会话cookie和session
		RequestCookieSession(request);
		// request请求参数及属性
		RequestParameter(request);
		// 请求转发
		RequestDispatcher(request, response);
	}

	/**
	 * 请求转发
	 */
	private void RequestDispatcher(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		f("============ RequestDispatcher ==================");
		// 转发的类型FORWARD,INCLUDE,REQUEST,ASYNC,ERROR
		// REQUEST代表来自客户端的请求
		// INCLUDE代表了当前(servlet、jsp、html)是被包含在其他(servlet、jsp、html)里的
		// FORWARD代表了由其他Servlet、jsp或html转发过来的
		// ASYNC
		// ERROR
		DispatcherType dispathcerType = request.getDispatcherType();
		f(dispathcerType);
		// 可以使用?id=111可以使用像get方法一样传送字符串查询参数
		RequestDispatcher dispatcher = request
				.getRequestDispatcher("/ms1?id=111");

		// 在include()或forward()时包括请求参数的作法,仅适用于传递字符串给另一个Servlet,在调派请求的过程中,如果由必须共享的“对象”
		// 可以设置给请求对象成为属性,称为请求范围属性。
		// 在使用了include或forward后,包含或转发Servlet则可以从request.getAttribute中取出这个值
		// 而response.sendRedirect不能取出值,因为它是二次访问
		request.setAttribute("nowDate", new Date());

		// include意为包含,即包含url中的内容,进一步理解为,将url中的内容包含进当前的servlet当中来,并用当前servlet的request和
		// respose来执行url中的内容处理业务.所以不会发生页面的跳转,地址栏地址不会发生改变。
		// 使用include()时,被包含的Servlet中任何对请求标头的设置都会被忽略,,被包含的Servlet中可以使用getSession()方法取得HttpSession对象
		// dispatcher.include(request, response);

		// forward是指转发,将当前request和response对象保存,交给指定的url处理。并没有表示页面的跳转,所以地址栏的地址不会发生改变。
		dispatcher.forward(request, response);

		// response.sendRedirect是指重定向,包含两次浏览器请求,浏览器根据url请求一个新的页面,所有的业务处理都转到下一个页面,
		// 地址栏的地址会变发生改变。
		// response.sendRedirect(request.getContextPath()+"/ms");

		// 如果在处理请求的过程中发现一些错误,而你想要传送服务器默认的状态与错误信息,可以使用sendError()方法。
		// 由于利用到HTTP状态码,要求浏览器重定向网页,因此,sendError()方法同样必须在未确认输出前执行,否则会抛出IllegalStateException
		// 可以使用HttpServletResponse来查询状态码
		// response.sendError(HttpServletResponse.SC_NOT_FOUND);
		// 当然可以自定义原因短语
		// response.sendError(HttpServletResponse.SC_NOT_FOUND, "找不到页面");
		f("分发后还执行我");
		f("============  RequestDispatcher end ==================");
	}

	/**
	 * 获取请求参数
	 * 
	 * @param request
	 * @throws UnsupportedEncodingException
	 */
	private void RequestParameter(HttpServletRequest request)
			throws UnsupportedEncodingException {
		f("============request parameter==================");
		// 得到编码方式,这个body内容的编码方式,及POST方式提交的编码方式。
		String characterEncoding = request.getCharacterEncoding();
		f(characterEncoding);
		// 得到所有参数值
		Enumeration<String> paramterNames = request.getParameterNames();
		while (paramterNames.hasMoreElements()) {
			String name = paramterNames.nextElement();
			// 根据参数名称得到参数值
			f(name + "=" + request.getParameter(name));
		}
		// 得到所有参数值,以map的形式存储,
		Map<String, String[]> parameterMap = (Map<String, String[]>) request
				.getParameterMap();
		for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
			System.out.print(entry.getKey() + "=");
			if (entry.getValue() != null && entry.getValue().length > 0) {
				for (String value : entry.getValue()) {
					System.out.print(value + ",");
				}
			}
		}
		f("");
		// 得到参数的多个值,比如多选框
		String[] parameterValues = request.getParameterValues("abc");
		if (parameterValues != null) {
			for (String pv : parameterValues) {
				// 没有用
				// String p = URLDecoder.decode(pv, "UTF-8");
				// 直接设置编码方式
				// String p = new String(pv.getBytes("ISO-8859-1"),"UTF-8");
				System.out.print(pv + ",");
			}
		}
		// 根据参数名称得到参数值
		String parameter = request.getParameter("id");
		f("");
		f(parameter);
		f("============request parameter end==================");
	}

	/**
	 * 会话功能 cookie 和session
	 * 
	 * @param request
	 */
	private void RequestCookieSession(HttpServletRequest request) {
		f("============cookies session ==================");
		// 取得所有Cookie
		Cookie[] cookies = request.getCookies();
		// 取得session
		HttpSession hs = request.getSession();
		// 如果当前session为空则创建一个,true为自动创建session
		HttpSession httpSession = request.getSession(true);
		// 当前的请求的sessionID,没有指定则返回null
		String sessionId = request.getRequestedSessionId();
		f("改变前的sessionId=" + sessionId);
		// request.changeSessionId();
		// sessionId = request.getRequestedSessionId();
		f("改变后的sessionId=" + sessionId);
		boolean sessionIdFromCookie = request.isRequestedSessionIdFromCookie();
		f("sessionID是从Cookie中来的?" + sessionIdFromCookie);
		boolean sessionIdFromURL = request.isRequestedSessionIdFromURL();
		f("sessionId是从URL来的?" + sessionIdFromURL);
		boolean sessionIdValid = request.isRequestedSessionIdValid();
		f("sessionId是有效的?" + sessionIdValid);
		f("============cookies session end==================");
	}

	/**
	 * 客户端地址信息和服务器端地址信息
	 * 
	 * @param request
	 */
	private void RequestLocalRemoteAddr(HttpServletRequest request) {
		f("============IP Local port==================");
		// 服务器的IP地址
		String localAddr = request.getLocalAddr();
		f(localAddr);
		// 服务器IP地址对应的域名
		String localName = request.getLocalName();
		f(localName);
		// 本地使用的端口号
		int port = request.getLocalPort();
		f(port);
		// 如果客户端提供了Accept-Language 值,则使用客户端提供的语言,否则默认就是用服务器设置的国际化语言
		Locale locale = request.getLocale();
		f(locale);
		// 和上面的不同之处在于这个是多个服务器语言,默认是使用服务器设置的本地语言
		Enumeration<Locale> enumLocale = request.getLocales();
		while (enumLocale.hasMoreElements()) {
			Locale loc = enumLocale.nextElement();
			f(loc);
		}
		// 客户端IP地址
		String remoteAddr = request.getRemoteAddr();
		f(remoteAddr);
		// 获得客户端的主机名,如果没有就获取IP地址
		String remoteHost = request.getRemoteHost();
		f(remoteHost);
		// 客户端端口号
		int remotePort = request.getRemotePort();
		f(remotePort);
		// 客户端登录用户信息
		String user = request.getRemoteUser();
		f(remotePort);
		f("============IP Local port end ==================");
	}

	/**
	 * 
	 * @param request
	 */
	private void RequestHeader(HttpServletRequest request) {
		// 得到请求中所有header的名称集合
		Enumeration<String> en = request.getHeaderNames();
		f("============Header==================");
		while (en.hasMoreElements()) {
			// 得到header名称
			String name = en.nextElement();
			// 取得header对应的值
			f(name + "=" + request.getHeader(name));
		}
		// 查询header中的时间戳,比如If-Modified-Since,,如果不存在返回-1
		long modified = request.getDateHeader("If-Modified-Since");
		f(modified);
		// 查询head中对应的App值
		Enumeration<String> getHeaders = request.getHeaders("App");
		while (getHeaders.hasMoreElements()) {
			// 得到header名称
			String name = getHeaders.nextElement();
			// 取得header对应的值
			f(name + "=" + request.getHeader(name));
		}
		// 查询Header头返回一个数值,如果不存在返回-1
		int intValue = request.getIntHeader("abc");
		f(intValue);
		f("============Header end==================");
	}

	/**
	 * reqeust请求行
	 * 请求报文请求服务器对资源进行一些操作。请求报文的起始行,或称为请求行,包含了一个方法和一个请求URL,这个方法描述了服务器应该执行的操作,
	 * 请求URL描述了要对那个资源执行这个方法。请求行中还包含HTTP的版本,用来告知服务器,客户端使用的是哪种HTTP。
	 */
	private void RequestLine(HttpServletRequest request) {
		f("============request line==================");
		// 请求使用的协议及版本
		String protocol = request.getProtocol();
		f(protocol);
		// 请求使用的方法比如GET POST
		String method = request.getMethod();
		f(method);
		f("============request line end==================");
	}

	/**
	 * 请求取得URL的所有信息
	 * 
	 * @param request
	 * @throws UnsupportedEncodingException
	 */
	private void Request_URL(HttpServletRequest request)
			throws UnsupportedEncodingException {
		f("============request URL ==================");
		// 实例http://localhost:8080/servlet-demo/myRequestServlet/test?id=123&abc=222
		// 方案实际上是规定如何访问制定资源的主要标识符,它会告诉负责解析URL的应用程序应该使用什么协议。
		// 方案组件必须以一个字母符号开始,有第一个“:”符号将其与URL的其余部分分隔开来。方案名是大小写无关的。
		String scheme = request.getScheme();
		// 输出:http
		f(scheme);
		// 主机组件表示了因特网上能够访问资源的宿主机器
		String serverName = request.getServerName();
		// 输出:localhost
		f(serverName);
		// 端口组件标识了服务器正在监听的网络端口。
		int serverPort = request.getServerPort();
		// 输出:8080
		f(serverPort);
		// 项目在web容器中的根路径,环境路径。如果应用程序环境路径和Web服务器环境根路径相同,则应用程序环境路径为空字符串,
		// 如果不是,则应用程序环境路径以“/”开头,不包括“/”结尾。
		String contextPath = request.getContextPath();
		// 输出:/servlet-demo
		f(contextPath);
		// 资源位于服务器的什么地方。路径通常很像一个分级的文件系统路径。
		// Servlet中资源路径
		String servletPath = request.getServletPath();
		// 输出:/myRequestServlet/test
		f(servletPath);
		// 通过提问题或者查询来缩小所请求资源类型的范围。
		// 查询字符串以一系列“名/值”对的形式出现,名值对之间用字符“&”分隔
		String queryString = request.getQueryString();
		// 输出:id=123&abc=222
		f(queryString);
		f(URLDecoder.decode(queryString, "UTF-8"));
		// 路径信息不包括请求参数,值的是不包括环境路径与Servlet路径部分的额外路径信息。
		// 如果没有额外路径信息,则为null(扩展映射、预设Servlet、完全匹配的情况下,getPathInfo()就会取得null)
		// 如果有额外路径信息,则是一个以“/”开头的字符串。
		String pathInfo = request.getPathInfo();
		f(pathInfo);
		// 请求的URI不包含方案主机名端口查询参数。仅包含地址
		String requestURI = request.getRequestURI();
		// 输出:/servlet-demo/myRequestServlet/test
		f(requestURI);
		// 请求的URL不包含查询参数
		String requestURL = request.getRequestURL().toString();
		// 输出:http://localhost:8080/servlet-demo/myRequestServlet/test
		f(requestURL);
		// 服务器端真实绝对路径资源位置
		String pathTranslated = request.getPathTranslated();
		f(pathTranslated);
		f("============request URL end==================");
	}

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

}

package com.zghw.servlet.demo;

import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 包装request的请求参数编码的转换
 * 
 * @author zghw
 *
 */
public class MyServletRequestWrapper extends HttpServletRequestWrapper {
	private static final String CHARSET = "UTF-8";

	public MyServletRequestWrapper(HttpServletRequest request) {
		super(request);
	}

	/**
	 * 重写get方法编码参数
	 */
	@Override
	public String getParameter(String name) {
		String paramter = super.getParameter(name);
		paramter = ("").equals(paramter) ? null : convert(paramter);
		return paramter;
	}

	@Override
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> map = super.getParameterMap();
		Map<String,String[]> wapper=new HashMap<String,String[]>();
		if (map != null) {
			for (Map.Entry<String, String[]> m : map.entrySet()) {
				String[] orgin = m.getValue();
				String[] values = convertArray(orgin);
				wapper.put(m.getKey(), values);
			}
		}
		return wapper;
	}

	@Override
	public String[] getParameterValues(String name) {
		String[] strs = super.getParameterValues(name);
		if (strs != null) {
			for (int i = 0; i < strs.length; i++) {
				strs[i] = convert(strs[i]);
			}
		}
		return strs;
	}

	private String[] convertArray(String[] orgin) {
		String[] value = null;
		if (orgin != null) {
			value = new String[orgin.length];
			int i = 0;
			for (String val : orgin) {
				value[i] = convert(val);
				i++;
			}
		}
		return value;
	}

	private String convert(String paramter) {
		if (paramter != null) {
			try {
				//设置请求参数的默认编码方式ISO-8859-1变为UTF-8
				return new String(paramter.getBytes("ISO-8859-1"), CHARSET);
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

}

package com.zghw.servlet.demo;

import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;

import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet1 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	public static void f(Object obj){
		System.out.println(obj);
	}
	public void init() throws ServletException {
		System.out.println("执行Servlet1 init()");
	}
	public void destroy() {
		System.out.println("执行Servlet1 destroy()");
    }
	public void doGet(HttpServletRequest request, HttpServletResponse respose)
			throws ServletException, IOException {
		System.out.println("执行Servlet1 service");
		DispatcherType dispathcerType = request.getDispatcherType();
		System.out.println("dispathcerType:"+dispathcerType);
		//取出上一个转发的所有request的属性名
		Enumeration<String> names = request.getAttributeNames();
		while(names.hasMoreElements()){
			String name = names.nextElement();
			//取出属性值
			f(name+":"+request.getAttribute(name));
		}
		String id = request.getParameter("id");
		f("paramter:id="+id);
		Date now=(Date)request.getAttribute("nowDate");
		f("得到上一个转发送来的值:nowDate = "+now);
		//FORWARD上一个servlet或JSP html的信息参数属性,对于FORWARD前一个请求需要做的事情。
		if(dispathcerType.equals(DispatcherType.FORWARD)){
			String contextPath=(String)request.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH);
			f("FORWARD:"+contextPath);
			String pathInfo= (String)request.getAttribute(RequestDispatcher.FORWARD_PATH_INFO);
			f("FORWARD:"+pathInfo);
			String queryString= (String)request.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
			f("FORWARD:"+queryString);
			String requestURI= (String)request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI);
			f("FORWARD:"+requestURI);
			String servletPath= (String)request.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH);
			f("FORWARD:"+servletPath);
		}
		//INCLUDE包含类型时,可以从参数中取得的类型
		if(dispathcerType.equals(DispatcherType.INCLUDE)){
			String contextPath= (String)request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH);
			f("INCLUDE:"+contextPath);
			String pathInfo= (String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
			f("INCLUDE:"+pathInfo);
			String queryString= (String)request.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING);
			f("INCLUDE:"+queryString);
			String requestURI= (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);
			f("INCLUDE:"+requestURI);
			String servletPath= (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
			f("INCLUDE:"+servletPath);
		}
		//ERROR类型时,可以从request.getAttribute();中取得需要的参数值
		if(dispathcerType.equals(DispatcherType.ERROR)){
			String errorException= (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
			String exceptionType= (String)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
			String errorMessage= (String)request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
			String requestURI= (String)request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
			String servletName= (String)request.getAttribute(RequestDispatcher.ERROR_SERVLET_NAME);
			String statusCode= (String)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
		}
	}
}

package com.zghw.servlet.demo;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyFilter implements Filter {

	// 取得过滤器配置参数供doFilter使用
	private FilterConfig filterConfig;

	/**
	 * FilterConfig包含了Filter配置的参数,可以得到ServletContext对象
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("执行MyFilter init");
		this.filterConfig = filterConfig;
		// 获取过滤器的配置参数
		Enumeration<String> paramNames = filterConfig.getInitParameterNames();
		while (paramNames.hasMoreElements()) {
			String name = paramNames.nextElement();
			System.out.println("过滤器配置的参数 name = " + name + " , value = "
					+ filterConfig.getInitParameter(name));
		}

		// 用来测试ServletContextAttributeListenter实现类作用,
		ServletContext servletContext = filterConfig.getServletContext();
		// 当添加属性值时,会通知监听器调用attributeAdded
		servletContext.setAttribute("test1",
				"test ServletContextAttributeListenter add in MyFilter ");
		// 当改变属性值时,会通知监听器调用attributeReplaced
		servletContext.setAttribute("test1",
				"test ServletContextAttributeListenter replace in MyFilter ");
		// 当改变属性值时,会通知监听器调用attributeRemoved
		servletContext.removeAttribute("test1");
	}

	/**
	 * 当请求来到容器,而容器发现调用Servlet的serivce方法前,可以应用某过滤器时,就会调用该过滤器的doFilter()方法,
	 * 可以在doFilter()方法中进行service()方法的前置处理,而后决定是否调用FilterChain的doFilter()方法。
	 * 如果调用了FilterChain的doFilter
	 * ()方法,就会运行下一个过滤器,如果没有下一个过滤器了,就调用请求目标Servlet的service()方法,
	 * 如果因为某个情况(如用户没有通过验证
	 * )而没有调用FilterChain的doFilter()方法,则请求就不会继续交给接下来的过滤器或目标Servlet,
	 * 这时候就是所谓的拦截请求(从Servlet的观点来看,根本不知道浏览器有发出请求)。
	 * 在陆续调用完FIlter实例的doFilter()仍至Servlet的service
	 * ()之后,流程会以堆栈顺序返回,所以在FilterChain的doFilter()运行完毕后, 就可以针对service()方法做后续处理。
	 * 
	 * 只需要知道FilterChain运行后会以堆栈顺序返回即可。在实现Filter接口时,不用理会这个Filter前后是否有其他Filter,
	 * 应该将之作为一个独立的元件设计。 Servlet/JSP提供的过滤器机制,其实是Java EE设计模式中Interceptor
	 * Filter模式的实现。如果希望可以弹性地抽换某功能地前置与后置处理元件 (例如Servlet/JSP
	 * 中Servlet的service()方法的前置与后置处理),就可以应用Interceptor Filter模式。
	 */
	@Override
	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		// POST方法参数编码为UTF-8
		request.setCharacterEncoding("UTF-8");
		if (request.getMethod().equals("GET")) {
			// 设置GET方法参数编码为UTF-8,通过装饰类MyServletRequestWrapper来改变request对象的请求参数编码
			request = new MyServletRequestWrapper(request);
		}
		System.out.println("执行MyFilter doFilter");
		System.out.println("执行MyFilter doFilter before");
		chain.doFilter(request, response);
		response.setCharacterEncoding("UTF-8");
		System.out.println("执行MyFilter doFilter after");
	}

	@Override
	public void destroy() {
		System.out.println("执行MyFilter destroy");
	}

}


你可能感兴趣的:(Request介绍及示例 PART1)