第4章 请求
request对象封装了所有来自客户端请求的信息。在HTTP协议中,这些信息以HTTP header和请求消息体的形式从客户端发送至服务器。
4.1 HTTP协议参数
Servlet的请求参数是一些由客户端发送给servlet容器的字符串,它是请求的一部分。当请求是HttpServletRequest对象且满足后文列出的条件时,容器从URI query字符串和POST提交的数据中封装这些参数。
参数存储成一组名-值对,同一个参数名称可以存在多个参数值。ServletRequest接口的下列方法用于访问这些参数:
• getParameter
• getParameterNames
• getParameterValues
getParameterValues方法返回一个String对象数组,包含和参数名关联的所有参数值。getParameter方法返回的值必须是getParameterValues返回的String对象数组的第一个值。
来自query字符串和post体的数据组成请求参数集。query字符串数据位于post体数据之前。比如,如果发出的请求由query字符串a=hello和post体a=goodbye&a=world组成,那么组成的参数集顺序为a=(hello, goodbye, world)。
这些API不会暴露作为GET请求(由HTTP 1.1定义)一部分的path参数。必须通过getRequestURI或者getPathInfo方法返回的字符串值进行解析才能获得。
4.1.1 参数何时可用
post表单数据被封装至参数集之前,必须满足下列条件:
1. 是HTTP或者HTTPS请求
2. HTTP方法是POST
3. 内容类型为application/x-www-form-urlencoded
4. servlet调用了request对象的任意一个getParameter系列方法。
如果条件不满足,post的表单数据不被包含在参数集中,但post数据仍然可以通过request对象的输入流获得。如果条件满足,post的表单数据将不再可以直接从request对象的输入流中获得。
4.2 属性
属性是一些和请求关联的对象。属性可以由容器设置来表示API所不能表示的信息,或者由servlet设置来和另一个servlet通信(通过RequestDispatcher)。属性可以通过ServletRequest接口的以下方法访问:
• getAttribute
• getAttributeNames
• setAttribute
一个属性值只能和一个属性名称关联。
,amed in accerse reverse domain 以“java.”和“javax.”为前缀的属性名称是本规范的保留定义。同样,以“sun.”和“com.sun.”为前缀的属性名称是Sun Microsystems公司的保留定义。建议属性集中的所有属性命名符合Java编程语言规范1中推荐的关于包命名的反向域名约定。
4.3 Header
servlet可以通过HttpServletRequest接口的下列方法访问HTTP 的请求header:
• getHeader
• getHeaders
• getHeaderNames
getHeader方法返回指定名称的header。在HTTP请求中同一名称可以有多个header,比如Cache-Control header。如果有同一个名称的多个header,getHeader方法返回请求中的第一个header。getHeaders方法允许访问与指定header名称关联的所有header值,以String对象的Enumeration形式返回。
header可能包含int或Date数据的字符串形式。HttpServletRequest接口的以下便捷方法提供了对这些格式header数据的访问:
• getIntHeader
• getDateHeader
如果getIntHeader方法不能将header值转换成int类型,抛出NumberFormatException异常。如果getDateHeader方法不能将header值转换成Date对象,抛出IllegalArgumentException异常。
4.4 请求路径组成
请求路径由许多重要部分组成。以下元素从请求URI路径中获得,并通过request对象暴露:
• 上下文路径(Context Path):servlet所在的ServletContext路径前缀。如果上下文是以web服务器URL命名空间的根为“default”上下文,那么该路径将是一个空字符串。否则,如果上下文不是以服务器命名空间的根为根,那么该路径以“/”开头,但不以“/”结束。
• Servlet路径(Servlet Path):这部分直接对应于激活该请求的映射。以“/”开头,除非以“/*”形式来匹配请求,这种情况下为空字符串。
• PathInfo:这部分请求路径不是上下文路径或Servlet路径的一部分。如果没有附加的路径,它为null,否则也可以是一个“/”开头的字符串。
HttpServletRequest接口的下列方法用来访问这些信息:
• getContextPath
• getServletPath
• getPathInfo
要注意这一点,不考虑请求URI和路径部分之间的URL编码差异,下列等式将总是为true:
requestURI = contextPath + servletPath + pathInfo
给出一些例子来明确上述观点:
表1:上下文设置示例
上下文路径 | /catalog |
Servlet映射 | Pattern: /lawn/* Servlet: LawnServlet |
Servlet映射 | Pattern: /garden/* Servlet: GardenServlet |
Servlet映射 | Pattern: *.jsp Servlet: JSPServlet |
行为如下:
表2:路径组成
请求路径 | 路径元素 |
/catalog/lawn/index.html | ContextPath: /catalog ServletPath: /lawn PathInfo: /index.html |
/catalog/garden/implements/ | ContextPath: /catalog ServletPath: /garden PathInfo: /implements/ |
/catalog/help/feedback.jsp | ContextPath: /catalog ServletPath: /help/feedback.jsp PathInfo: null |
4.5 路径转译方法
API中有两个便捷方法让开发人员获得指定路径对应的文件系统路径。这些方法是:
• ServletContext.getRealPath
• HttpServletRequest.getPathTranslated
getRealPath方法接受一个String参数,返回路径对应的本地文件系统表示文件的String形式。getPathTranslated方法计算出请求pathInfo的真实路径。
有些情况,servlet容器不能为这些方法判断是否文件路径有效,比如web应用以war的形式运行,或者是本地不能访问的远程文件系统,或者位于数据库中,这些方法都会返回null。
4.6 Cookie
HttpServletRequest接口提供getCookies方法获得请求中的cookie数组。cookie是一些在客户端每次请求中由客户端发送给服务器的数据。通常客户端cookie名和cookie值的形式发送回。当cookie被发送回浏览器时设置的一些其他cookie,比如注释,通常不会返回。
4.7 SSL属性
如果请求基于安全协议传输,比如HTTPS,这些信息必须通过ServletRequest接口的isSecure方法来暴露。Web容器必须将下列属性暴露给servlet程序员:
表3:协议属性
属性 | 属性名 | Java类型 |
cipher套件 | javax.servlet.request.cipher_suite | String |
算法的位大小 | javax.servlet.request.key_size | Integer |
如果请求和SSL认证关联,那么它必须由servlet容器以java.security.cert.X509Certificate类型的对象数组形式暴露给servlet程序员,通过javax.servlet.request.X509Certificate的ServletRequest属性访问。
数组中的顺序定义为按可信任度升序排列。链表中的第一个certificate由客户端设置,下一个用来对第一个进行认证,以此类推。
4.8 国际化
客户端可能向web服务器提出它们倾向接受哪种语言的响应。这些信息可以由客户端使用Accept-Language header进行通信,这和HTTP/1.1规范描述的其他机制一样。ServletRequest接口提供下列方法来确定发送者倾向的locale:
• getLocale
• getLocales
getLocale方法返回客户端接收内容时倾向采取的locale。关于如何解释Accept-Language header以确定客户端倾向语言的更多信息,参见RFC 2616 (HTTP/1.1)14.4节。
getLocales方法返回客户端可接受的locale的Locale对象的Enumeration形式,它以locale的接受程度降序排列。
如果客户端未指定所倾向的locale,getLocale方法必须返回servlet容器的默认locale,getLocales方法必须包含一个默认locale的Locale元素的Enumeration形式。
4.9 请求数据编码
目前,许多浏览器不会使用Content-Type header限定字符编码,而让客户端来决定读取HTTP请求时采取的字符编码。如果客户端请求中未指定,容器使用默认的“ISO-8859-1” 编码来创建请求请求reader,解析POST数据。不过为了提示开发人员这种发送字符编码的错误,容器将让getCharacterEncoding方法返回null。
如果客户端没有设置字符编码,并且请求数据的编码和上述描述的默认编码不一致,则可能会发生数据损坏。为了避免这种情况,ServletRequest增加了一个新方法,setCharacterEncoding(String enc)。开发人员可以通过调用该方法重新设定容器所支持的字符编码。它必须在解析post数据或者从请求读取输出之前调用。一旦数据已被读取,调用该方法不会影响编码。
4.10 Request对象的生命周期
每一个request对象只在servlet的service方法作用域,或者filter的doFilter方法作用域内有效。容器通常循环request对象以避免request对象创建所引起的性能开销。开发人员必须认识到在上述的作用域之外对request对象进行引用可能会导致无法预期的后果。
1. Java编程语言规范http://java.sun.com/docs/books/jls