Servlet规范之The Request

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet规范之The Request相关的知识,希望对你有一定的参考价值。

The Request

文章是对 JSR-000340 JavaTM Servlet 3.1 Final Release的Java™ Servlet规范的翻译,尚未校准

文章目录


请求对象封装了来自客户端请求的所有信息。在HTTP协议中,这些信息在HTTP头和请求的信息体中从客户端传输到服务器。

HTTP协议参数

Servlet的请求参数是由客户端发送至Servlet容器的字符串,作为其请求的一部分。当请求是一个HttpServletRequest对象,并且符合第24页 "When Parameters Are Available"中规定的条件时,容器会从URI查询字符串和POST-ed数据中填充参数。

参数被存储为一组name-value对。对于任何给定的参数名称,可以存在多个参数值。ServletRequest接口的下列方法可用于访问参数。

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

getParameterValues方法返回一个字符串对象数组,其中包含与参数名称相关的所有参数值。从getParameter方法返回的值必须是getParameterValues返回的String对象阵列中的第一个值。getParameterMap方法返回请求的参数的java.util.Map,它包含作为键的名称和作为映射值的参数值。

来自查询字符串和帖子正文的数据被汇总到请求参数集。查询字符串的数据会在帖子正文的数据之前呈现。例如,如果一个请求的查询字符串为a=hello,帖子正文为a=goodbye&a=world,那么产生的参数集将被排序为a=(hello, goodbye, world)

作为GET请求的一部分的路径参数(如HTTP 1.1所定义的)不被这些API所公开。它们必须从getRequestURI方法或getPathInfo方法返回的String值中解析出来。

When Parameters Are Available

以下是必须满足的条件,然后才会将表单数据填充到参数集中。

  1. 该请求是一个HTTP或HTTPS请求。
  2. HTTP方法是POST。
  3. 内容类型是 application/x-www-form-urlencoded
  4. servlet对请求对象的 getParameter系列方法进行了初始调用。

如果不满足这些条件,并且post form数据不包括在参数集中,那么post数据仍然必须通过请求对象的输入流提供给servlet。如果条件得到满足,POST数据将不再可用于直接从请求对象的输入流中读取。

文件上传

Servlet容器允许在数据以multipart/form-data形式发送时上传文件。

如果满足以下任何一个条件,Servlet容器就会提供multipart/form-data处理。

  • 处理请求的servlet被注释为@MultipartConfig,定义在第8-70页的第8.1.5节"@MultipartConfig"。
  • 部署描述符包含处理请求的Servlet的multipart-config元素。

如何提供multipart/form-data类型的请求中的数据,取决于servlet容器是否提供multipart/form-data处理:

  • 如果servlet容器提供了multipart/form-data处理,数据将通过HttpServletRequest中的下列方法提供:

    • public Collection<Part> getParts()
    • public Part getPart(String name)

    每个part都可通过Part.getInputStream方法访问头部,相关的内容类型和内容。

    对于以表单数据的Content-Disposition的部分,即使没有文件名,该部分的字符串值也将通过HttpServletRequest上的getParametergetParameterValues方法得到该部分的名称。

  • 如果servlet容器不提供 multi-part/form-data处理,数据将通过HttpServletReuqest.getInputStream来获得。

Attributes

属性是与一个请求相关的对象。属性可以由容器设置,以表达无法通过API表达的信息,也可以由Servlet设置,以向另一个Servlet传递信息(通过RequestDispatcher)。属性可以通过ServletRequest接口的下列方法来访问:

  • getAttribute
  • getAttributeNames
  • setAttribute

一个属性名称只能关联一个属性值。

java. javax.开头的属性名被保留给本规范定义。同样,以sun.com.sun.oraclecom.oracle开头的属性名也被保留给Oracle公司定义。建议放在属性集中的所有属性都按照《Java编程语言规范》建议的反向域名惯例来命名包。

Headers

一个servlet可以通过HttpServletRequest接口的以下方法访问HTTP请求的头信息:

  • getHeader
  • getHeaders
  • getHeaderNames

getHeader方法返回一个给定的头的名称。在一个HTTP请求中可以有多个同名的头,例如Cache-Control头。如果有多个同名的头信息,getHeader方法会返回请求中的第一个头信息。getHeaders方法允许访问所有与特定头名称相关的头值,返回一个Enumeration的字符串对象。

头信息可以包含 intDate数据的String表示。HttpServletRequest接口的下列方便方法提供了对这些格式之一的头数据的访问:

  • getIntHeader
  • getDateHeader

如果getIntHeader方法不能将头的值翻译成int,就会抛出NumberFormatException。如果getDateHeader方法不能将标题翻译成Date对象,就会抛出IllegalArgumentException

Request Path 元素

服务请求的servlet的请求路径由许多重要部分组成。以下元素从请求URI路径中获得,并通过请求对象暴露:

  • Context Path: 与ServletContext相关的路径前缀,该Servlet是其中的一部分。如果这个上下文是基于Web服务器的URL名称空间的 "default"上下文,这个路径将是一个空字符串。否则,如果该上下文没有基于服务器名称空间的根目录,那么该路径以一个/字符开始,但不以一个/字符结束。
  • Servlet Path: 直接对应于激活该请求的映射的路径部分。这个路径以/字符开始,除非请求与/*或" "模式匹配,在这种情况下,它是一个空字符串。
  • PathInfo: 请求路径中不属于Context Path或Servlet Path的部分。如果没有额外的路径,它就是空的,或者是一个带/的字符串。

使用HttpServletRequest接口中的下面方法来访问这些信息:

  • getContextPath
  • getServletPath
  • getPathInfo

需要注意的是,除了请求URI和路径部分的URL编码不同,下面的等式总是正确的:

requestURI = contextPath + servletPath + pathInfo

举几个例子来说明上述观点,请考虑以下几点:

TABLE 3-1 Example Context Set Up

ContextPath/catalog
ServletMappingPattern: /lawn/*
Servlet: LawnServlet
ServletMappingPattern: /garden/*
Servlet: GardenServlet
ServletMappingPattern: *.jsp
Servlet: JSPServlet

The following behavior is observed:

TABLE 3-2 Observed Path Element Behavior

Request PathPath Elements
/catalog/lawn/index.htmlContextPath: /catalog
ServletPath: /lawn
PathInfo: /index.html
/catalog/garden/implements/ContextPath: /catalog
ServletPath: /garden
PathInfo: /implements/
/catalog/help/feedback.jspContextPath: /catalog
ServletPath: /help/feedback.jsp
PathInfo: null

路径转换方法

在API中有两个方便的方法,允许开发者获得相当于特定路径的文件系统路径。这些方法是:

  • ServletContext.getRealPath
  • HttpServletRequest.getPathTranslated

getRealPath方法接受一个String参数,并返回一个路径所对应的本地文件系统中的文件的String表示。getPathTranslated方法计算请求的pathInfo的真实路径。

在Servlet容器无法为这些方法确定有效的文件路径的情况下,例如,当Web应用程序从归档文件、在本地无法访问的远程文件系统或在数据库中执行时,这些方法必须返回空。JAR文件的META-INF/resources目录下的资源,只有在调用getRealPath()时,容器已经将其从包含的JAR文件中解压,才必须考虑,在这种情况下,必须返回解压的位置。

Non Blocking IO

Web容器中的非阻塞请求处理有助于改善不断增长的对改进Web容器可扩展性的需求,增加Web容器可同时处理的连接数。Servlet容器中的非阻塞IO允许开发者在数据可用时读取数据,或在可能时写入数据。非阻塞IO只适用于Servlet和过滤器中的异步请求处理(如第2-10页第2.3.3.3节 "异步处理 "中的定义),以及升级处理(如第2-20页第2.3.3.5节 "升级处理 "中的定义)。否则,当调用ServletInputStream.setReadListenerServletOutputStream.setWriteListener时,必须抛出一个IllegalStateException

The ReadListener provides the following callback methods for non blocking IO

ReadListener为非阻塞性IO提供以下回调方法:

  • ReadListener
    • onDataAvailable(). 当可以从传入的请求流中读取数据时,onDataAvailable方法会被调用到 ReadListener上。容器将在数据可被读取时第一次调用该方法。容器随后将调用onDataAvailable方法,当且仅当ServletInputStream上的isReady方法(如下所述)返回false
    • onAllDataRead(). 当你完成了对注册了监听器的ServletRequest的所有数据的读取时,onAllDataRead方法被调用。
    • onError(Throwable t). 如果在处理请求时有任何错误或异常,onError方法会被调用。

Servlet容器必须以线程安全的方式访问ReadListener中的方法。

除了上面定义的 ReadListener之外,在ServletInputStream类中还增加了以下方法:

  • ServletInputStream
    • boolean isFinished(). 当所有与 ServletInputStream相关的请求数据被读取时,isFinished方法返回true。否则它将返回false
    • boolean isReady(). isReady方法返回true,如果数据可以无阻塞地被读取。如果没有数据可以在不阻塞的情况下被读取,则返回 “false”。如果isReady返回false,调用read方法是非法的,必须抛出一个IllegalStateException
    • void setReadListener(ReadListener listener). 设置上面定义的ReadListener,使其被调用,以非阻塞的方式读取数据。一旦监听器与给定的ServletInputStream相关联,容器就会在数据可以读取、所有数据都已读取或处理请求时出现错误时调用ReadListener的方法。注册一个ReadListener将启动非阻塞的IO。此时切换到传统的阻塞式IO是非法的,必须抛出一个IllegalStateException。在当前请求的范围内对setReadListener的后续调用是非法的,必须抛出IllegalStateException

Cookies

HttpServletRequest接口提供了getCookies方法来获取请求中存在的cookies数组。这些cookie是在客户端发出的每个请求中从客户端发送到服务器的数据。通常情况下,客户端作为cookie的一部分发回的唯一信息是cookie名称和cookie值。当cookie被发送到浏览器时可以设置的其他cookie属性,如评论,通常不被返回。该规范还允许cookies为HttpOnlycookies。HttpOnlycookie向客户端表明,它们不应该被暴露给客户端脚本代码(除非客户端知道要寻找这个属性,否则它不会被过滤掉)。使用HttpOnly cookies有助于减轻某些类型的跨站脚本攻击。

SSL Attributes

如果一个请求是通过安全协议(如HTTPS)传输的,这一信息必须通过ServletRequest接口的isSecure方法公开。Web容器必须向Servlet程序员公开以下属性:

TABLE 3-3 Protocol Attributes

AttributeAttribute NameJava Type
cipher suitejavax.servlet.request.cipher_suiteString
bit size of the algorithmjavax.servlet.request.key_sizeInteger
SSL session idjavax.servlet.request.ssl_session_idString

如果有一个与请求相关的SSL证书,它必须由servlet容器暴露给servlet程序员,作为java.security.cert.X509Certificate类型的对象数组,并通过javax.servlet.request.X509CertificateServletRequest属性访问。

这个数组的顺序被定义为按照信任度的升序排列。链中的第一个证书是由客户设置的,下一个是用来验证第一个的,以此类推。

国际化

客户端可以选择向网络服务器表明他们希望响应的语言。这个信息可以从客户端使用Accept-Language标头和HTTP/1.1规范中描述的其他机制来传达。ServletRequest接口中提供了以下方法来确定发送者的首选语言。

  • getLocale
  • getLocales

getLocale方法将返回客户想要接受的内容的首选地区。参见RFC 2616 (HTTP/1.1)第14.4节,了解更多关于如何解释Accept-Language头以确定客户端的首选语言。

getLocales方法将返回一个Locale对象的枚举,按照从首选语言开始的递减顺序,指出客户可以接受的语言。

如果客户没有指定首选的locale,getLocale方法返回的locale必须是servlet容器的默认locale,getLocales方法必须包含默认locale的一个Locale元素的枚举。

请求数据的编码

目前,许多浏览器并不随Content-Type头发送 char编码修饰符,这就使读取HTTP请求的字符编码的确定成为可能。如果客户端请求没有指定编码,容器用来创建请求阅读器和解析POST数据的默认编码必须是 “ISO-8859-1”。然而,为了向开发者表明,在这种情况下,客户端未能发送字符编码,容器会从getCharacterEncoding方法中返回null

如果客户端没有设置字符编码,而请求数据的编码与上述的默认编码不同,就会发生中断。为了补救这种情况,一个新的方法setCharacterEncoding(String enc)已经被添加到ServletRequest接口中。开发者可以通过调用这个方法来覆盖由容器提供的字符编码。它必须在解析任何职位数据或从请求中读取任何输入之前被调用。一旦数据被读取,调用此方法将不会影响编码。

请求对象的生命周期

每个请求对象只在servlet的service方法的范围内有效,或者在过滤器的doFilter方法的范围内有效,除非组件的异步处理被启用,并且startAsync方法被调用到请求对象上。在异步处理发生的情况下,请求对象仍然有效,直到completeAsyncContext上被调用。容器通常会回收请求对象,以避免创建请求对象的性能开销。开发者必须意识到,不建议在上述范围之外维护对startAsync未被调用的请求对象的引用,因为这可能会产生不确定的结果。

在升级的情况下,上述情况仍然适用。

以上是关于Servlet规范之The Request的主要内容,如果未能解决你的问题,请参考以下文章

Servlet编程专题8之Servlet规范中的监听器

The process of container handles the servlet request

Java实战之04JavaWeb-07Listener和Filter

Servlet规范之预览

Servlet规范之Servlet顶层接口

Servlet规范之Servlet Context