03_Request

The Request

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

1. HTTP协议参数

servlet的请求参数是作为请求的一部分从客户机向servlet容器发送的字符串。当请求是一个HttpServletRequest对象时,在第24页上“当参数可用时”的条件被满足时,容器将从URI查询字符串和POST-ed数据中填充参数。

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

  • getParameter
  • getParameterNames
  • getParameterValues
  • getParameterMap

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

查询字符串和post主体的数据被聚合到请求参数集中,在post body数据之前显示查询字符串数据。例如,如果一个请求是用一个a=hello的查询字符串和一个a=goodbye&a=world的post体做的,那么结果的参数集将被排序为=(hello, goodbye, world).

GET请求(由HTTP 1.1定义)的一部分的路径参数没有被这些api公开。它们必须从getRequestURI方法或getPathInfo方法返回的字符串值中解析。

1.1 当参数可用时

The following are the conditions that must be met before post form data will be populated to the parameter set:
下面是在将表单数据填充到参数集之前必须满足的条件:

  1. The request is an HTTP or HTTPS request.
  2. The HTTP method is POST.
  3. The content type is application/x-www-form-urlencoded.
  4. The servlet has made an initial call of any of the getParameter family of methods on the request object.
    如果条件没有满足,并且在参数集中不包含post表单数据,那么post数据仍然必须通过请求对象的输入流对servlet可用。如果条件满足,则不再可以从请求对象的输入流直接读取post表单数据。

2.文件上传

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

如果满足以下任一条件,servlet容器将提供multipart/form-data处理。

■servlet处理的请求注释@MultipartConfig 8.1.5节的定义,“@MultipartConfig”8 - 74页。

■部署描述符包含servlet multipart-config元素处理请求。

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

■如果servlet容器提供了multipart/form-data处理,数据是通过以下在HttpServletRequest的方法可用的:
■ public Collection<Part> getParts()
■ public Part getPart(String name)

Each part provides access to the headers, content type related with it and the content via the Part.getInputStream method.
每个部分都提供了对header的访问,内容类型与它相关,而且内容来自Part.getInputStream方法。

对于以表单数据作为内容配置的部分,但是如果没有文件名,该部分的字符串值也可以通过使用该部分名称的HttpServletRequest的etParameter和getParameterValues方法获得。

每个部分都提供了对header的访问,内容类型与它相关,内容通过部分。getInputStream方法。

对于以表单数据作为内容配置但是没有文件名的部分,则该部分的字符串值也将通过HttpServletRequest的getParameter和getParameterValues方法获得,并使用该部分的名称。
■如果servlet容器不提供multi-part/form-data处理,可用的数据将通过HttpServletReuqest.getInputStream。

3.属性

属性是与请求相关联的对象。属性可以由容器设置,以表达通过API无法表达的信息,也可以由servlet设置以与另一个servlet进行信息交流(通过RequestDispatcher)。可使用ServletRequest接口的下列方法访问属性:
■ getAttribute
■ getAttributeNames
■ setAttribute
只有一个属性值可能与属性名相关联。以java和javax的前缀开始的属性名称是本规范的保留定义。类似地,属性名称以sun., com.sun., oracle and com.oracle的前缀开始,是由甲骨文公司保留的。建议将属性集中的所有属性按照Java编程语言规范所建议的反向域名约定命名。

4.信息头

servlet可以通过HttpServletRequest接口的以下方法访问HTTP请求的头:
■ getHeader
■ getHeaders
■ getHeaderNames

getHeader方法返回给定标题名称的标题。在HTTP请求中,可以有多个具有相同名称的头,例如缓存控制头。如果有多个具有相同名称的头,则getHeader方法将返回请求中的第一个头。getheader方法允许访问与特定头名称关联的所有头值,返回字符串对象的枚举。

头可能包含整数或日期数据的字符串表示。HttpServletRequest接口的以下便利方法提供了访问头数据的一种格式:
■getIntHeader
■getDateHeader
如果getIntHeader方法无法将标题值转换为int,则抛出NumberFormatException。如果getDateHeader方法不能将消息头转换为日期对象,则抛出一个IllegalArgumentException。

5.请求路径元素

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

■Context Path:与 ServletContext 相关联的路径前缀是这个 servlet 的一部分。如果这个上下文是基于 Web服务器的 URL 命名空间基础上的“默认”上下文,那么这个路径将是一个空字符串。否则,如果上下文不是
基于服务器的根命名空间,那么这个路径以/字符开始,但不以/字符结束。

■ Servlet Path:路径部分直接对应于激活此请求的映射。此路径以“/”字符开头,除非请求与“/*”或“”模式匹配,在这种情况下,它是一个空字符串。

■ PathInfo: 请求路径的一部分,不是上下文路径或Servlet路径的一部分。如果没有额外的路径,则为null,或者是一个带引导' / '的字符串。

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

■ getContextPath
■ getServletPath
■ getPathInfo

重要的是要注意,除了请求 URI 和路径部分的 URL 编码差异外,下面的等式永远为真:
requestURI = contextPath + servletPath + pathInfo
举几个例子来澄清上述各点,请考虑以下几点:

表 3-1 上下文设置的例子

Context Path /catalog
Servlet Mapping Pattern: /lawn/* Servlet: LawnServlet
Servlet Mapping Pattern: /garden/* Servlet: GardenServlet
Servlet Mapping Pattern: *.jsp Servlet: JSPServlet

遵守下列行为:

表 3-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

6. 路径转换方法

开发人员获得与特定路径等效的文件系统路径。这些方法有:

■ServletContext.getRealPath

■HttpServletRequest.getPathTranslated

getRealPath方法接受一个字符串参数,并返回路径对应的本地文件系统上的文件的字符串表示。getpathtransform方法计算请求的pathInfo的实际路径。

在servlet容器无法通过这些方法确定的文件有效路径的情况下,例如,当Web应用程序从存档中执行时,在本地无法访问的远程文件系统上,或者在数据库中,这些方法必须返回null。当容器在调用getRealPath()时从包含的JAR文件中解压它们,必须考虑到JAR文件的META-INF/ Resources目录中的资源,并且在这种情况下必须返回未解压的位置。

7.非阻塞IO

Web 容器中的非阻塞请求处理有助于提高对改善 Web 容器可扩展性不断增加的需求,增加 Web 容器可同时处理请求的连接数量。servlet 容器的非阻塞 IO 允许开发人员在数据可用时读取数据或在数据可写时写数据。

非阻塞 IO 仅对在 Servlet 和 Filter(2.3.3.3 节定义的,“异步处理”)中的异步请求处理和升级处理
(2.3.3.5 节定义的,“升级处理”)有效。否则,当调用 ServletInputStream.setReadListener 或ServletOutputStream.setWriteListener 方法时将抛出 IllegalStateException。

ReadListener 为非阻塞 IO 提供了下面的回调方法:

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

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

除了上述 ReadListener 定义的方法外,下列方法已被添加到 ServletInputStream 类中:

■ ServletInputStream
■ boolean isFinished(). 与 ServletReader/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。

8.HTTP/2服务器推送

服务器推送是HTTP/2中出现在servlet API中的最明显的改进。HTTP/2中的所有新特性,包括服务器推送,都是为了提高web浏览体验的性能。服务器推送的贡献在于改进了感知浏览器的性能,从简单的事实来看,在了解额外的资源(例如图像、样式表和脚本)与初始请求的关系上服务器的处境要比客户机好得多。例如,服务器可能知道,无论何时浏览器请求index.html,它不久将请求header.gif,footer.gif和style.css。因为服务器知道这一点,所以他们可以预先伴随发送index.html的字节开始发送这些资源的字节。

要使用服务器推送,可以从HttpServletRequest中获取一个PushBuilder的引用,根据需要将构建器进行mutate,然后调用push()。请参阅方法的javadoc javax.servlet.http.HttpServletRequest.newPushBuilder javax.servlet.http()和类。规范规范的PushBuilder。本节的其余部分将在第6页“其他重要参考”中引用的HTTP/2规范版本中的“服务器推送”一节中对实现要求进行调用。

除非明确排除,Servlet 4.0容器必须支持HTTP/2规范部分“服务器推送”中指定的服务器推送。如果客户端能够使用HTTP/2,那么容器必须启用服务器推送,除非客户端通过发送SETTINGS_ENABLE_PUSH设置值(0)来实现当前连接,从而禁用了服务器推送。在这种情况下,仅针对该连接,不能启用服务器推送。

除了允许客户端使用SETTINGS_ENABLE_PUSH设置禁用服务器推送之外,servlet容器还必须遵守客户机的请求,以避免在更细粒度的基础上接收被推的响应,因为它会注意到引用被推流的流标识符的CANCEL或拒绝流代码。这种交互的一个常见用法是,当浏览器已经拥有缓存中的资源时。

9. Cookies

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

10. SSL 属性

如果在安全协议(如HTTPS)上传输了请求,则必须通过ServletRequest接口的isSecure方法公开该信息。Web容器必须向servlet程序员公开以下属性:

TABLE 3-3 Protocol Attributes

Attribute Attribute Name Java Type
cipher suite javax.servlet.request.cipher_suite String
bit size of the algorithm javax.servlet.request.key_size Integer
SSL session id javax.servlet.request.ssl_session_id String

如果有与请求相关联的SSL证书,那么它必须由servlet容器公开给servlet程序员作为java.security.cert类型的对象数组。X509Certificate javax.servlet.request.X509Certificate通过ServletRequest属性。

这个数组的顺序被定义为以信任的升序排列。链中的第一个证书是由客户端设置的,第二个证书是用来对第一个证书进行身份验证的,以此类推。

11.国际化

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

  • getLocale
  • getLocales

getLocale方法将返回客户机想要接受内容的首选语言环境。请参阅RFC 7231 (HTTP/1.1)的第14.4节,了解关于如何解释Accept-Language头的更多信息,以确定客户机的首选语言。

getLocales方法将返回一个Locale对象的枚举,以减少订单开始时的首选语言环境,客户可以接受的区域。

如果客户机没有指定首选语言环境,则getLocale方法返回的语言环境必须是servlet容器的缺省语言环境,而getLocales方法必须包含缺省语言环境的单个语言环境元素的枚举。

12. 请求数据编码

目前,许多浏览器都不发送带有ContentType头的字符编码限定符,将字符编码的确定留给读取HTTP请求。在没有字符编码限定符的情况下,如果内容类型是application/x-www-form-urlencode,则容器用于创建请求读取器和解析POST数据的默认编码必须是US-ASCII。任何%nn编码的值必须被解码为ISO-8859-1。对于任何其他内容类型,如果客户端请求、web应用程序或容器供应商的特定配置(对于容器中的所有web应用程序)都没有指定,则容器用于创建请求读取器和解析POST数据的默认编码必须是ISO-8859-1。但是,为了向开发人员表明没有字符编码限定符,容器必须从getCharacterEncoding()方法返回null。

如果客户端没有设置字符编码,并且请求数据用与上面描述的默认编码不同的编码进行编码,那么就会发生破坏。来补救这种情况,ServletContext上可使用setRequestCharacterEncoding(String enc),web.xml使用的< request-character-encoding >元素以及在ServletRequest接口上可以使用setCharacterEncoding(String enc)。开发人员可以通过调用这个方法重写容器提供的字符编码。必须在解析任何post数据或从请求中读取任何输入之前调用它。一旦数据被读取,调用此方法将不会影响编码。

13.请求对象的生存期

除非为组件启用了异步处理,并且在请求对象上调用startAsync方法,否则每个请求对象仅在servlet的service方法的范围内,或者在过滤器的doFilter方法的范围内有效。在异步处理发生的情况下,请求对象仍然有效,直到在AsyncContext上调用complete为止。容器通常回收请求对象,以避免请求对象创建的性能开销。开发人员必须意识到,不建议使用startAsync在上面描述的范围之外的请求对象的引用,因为它可能有不确定的结果。

在升级的情况下,以上的说法依旧正确。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,136评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,493评论 18 399
  • 来到这世间是为了什么 这一生想得到的又是什么 责任担当成功 还是说过得有意义 呱呱坠地的一开始 我们就被扣上了很多...
    hello敏敏阿阅读 221评论 0 0
  • 掌心日记阅读 129评论 0 0