05_Response

The Response

响应对象封装从服务器返回到客户机的所有信息。在HTTP协议中,该信息通过HTTP头或请求的消息体从服务器传输到客户机。

1.缓冲

servlet容器被允许,但不是必需的,以缓冲输出到客户端以达到效率目的。通常使用缓冲的服务器使其成为默认的,但是允许servlet指定缓冲参数。

ServletResponse接口中的以下方法允许servlet访问和设置缓冲信息:

  • getBufferSize
  • setBufferSize
  • isCommitted
  • reset
  • resetBuffer
  • flushBuffer

这些方法是在ServletResponse接口上提供的,以允许在servlet使用ServletOutputStream或Writer时执行缓冲操作。

getBufferSize方法返回正在使用的底层缓冲区的大小。如果没有使用缓冲,则该方法必须返回0的int值。

servlet可以通过使用setBufferSize方法请求首选缓冲区大小。所分配的缓冲区不需要是servlet所请求的大小,但必须至少与所请求的大小相同。这允许容器重用一组固定大小的缓冲区,如果合适的话,可以提供比请求更大的缓冲区。在使用ServletOutputStream或Writer编写任何内容之前,必须调用该方法。如果已经写入了任何内容或响应对象,则该方法必须抛出IllegalStateException。

isCommitted方法返回一个布尔值,指示是否已将任何响应字节返回给客户端。flushBuffer方法强制将缓冲区中的内容写入客户机。

当响应未提交时,reset方法清除缓冲区中的数据。在重置调用之前,servlet所设置的标题、状态代码和调用getWriter或getOutputStream的状态也必须被清除。如果在没有清除标头和状态代码的情况下响应没有提交,则resetBuffer方法将清除缓冲区中的内容。

如果响应已提交,并调用reset或resetBuffer方法,则必须抛出IllegalStateException。响应及其相关缓冲区将保持不变。

当使用缓冲区时,容器必须立即将填充缓冲区的内容刷新到客户端。如果这是发送给客户机的第一个数据,则认为响应是提交的。

2. Headers

Servlet可以使用以下方法设置HTTP响应头:

  • setHeader
  • addHeader

setHeader方法用一个具有给定名称和值设置头。前面的header被新的header所代替。如果名称对于存在了一组header值,则将这些值清除并替换为新值。

addHeader方法为带有给定名称的集合添加了一个header值。如果没有与名称关联的标头,就会创建一个新集合。

header可能包含表示int或Date对象的数据。HttpServletResponse接口的以下便利方法允许servlet为适当的数据类型设置正确的格式:

  • setIntHeader
  • setDateHeader
  • addIntHeader
  • addDateHeader

要成功地传输回客户端,必须在提交响应之前设置headers(而不是trailer)。在响应提交后设置的头(非挂载)将被servlet容器忽略。如果在RFC 7230中指定的HTTP trailer将在响应中被发送,则必须使用HttpServletResponse的setTrailerFields()方法来提供。此方法必须在已写入的chunked响应中的最后一个块之前调用。

Servlet程序员负责确保为Servlet生成的内容在响应对象中适当地设置content-type头。HTTP 1.1规范不要求在HTTP响应中设置这个头。当Servlet程序员不设置类型时,Servlet容器不能设置默认的内容类型。

建议容器使用X-Powered-By HTTP头来发布其实现信息。字段值应该包含一个或多个实现类型,例如“Servlet/4.0”。可选地,容器和底层Java平台的补充信息可以在括号内的实现类型之后添加。容器应可配置以抑制此报头。

这里是此header的示例:

X-Powered-By: Servlet/4.0
X-Powered-By: Servlet/4.0 JSP/2.3 (GlassFish Server Open Source
Edition 5.0 Java/Oracle Corporation/1.8)

3.HTTP Trailer

HTTP trailer是一种特殊类型的HTTP报头,它是在响应主体之后出现的。trailer是在RFC 7230中指定的。它们在分块传输编码和其他通信协议的实现中很有用。Servlet容器为trailer提供支持。

如果trailer头已经准备好读取,isTrailerFieldsReady()将返回true。然后,servlet可以通过HttpServletRequest接口的getTrailerFields()方法读取HTTP请求的预告头。

servlet可以通过为HttpServletResponse接口的setTrailerFields方法提供一个供应商来编写响应。可以通过访问HttpServletResponse接口的getTrailerFields()方法获得trailer头的供应商。

关于规范规范的这两种方法,请参见javadoc。

4.非阻塞IO

非阻塞IO只适用于servlet和过滤器中的异步请求处理(如第2.3.3.3节中定义的“异步处理”页2-10)和升级处理(如第2.3.3.5节中定义的“升级处理”在第2-21页)。否则,必须在ServletInputStream.setReadListener或ServletOutputStream.setWriteListener被调用时抛出IllegalStateException。为了支持Web容器中的非阻塞写入,除了第3.7节中所描述的ServletRequest中所做的更改之外,在第3-28页上“非阻塞IO”,还进行了以下更改以处理响应相关的类/接口。

WriteListener提供了以下回调方法,容器可以适当地调用这些方法。

  • WriteListener
    • void onWritePossible()
      当一个WriteListener注册到ServletOutputStream时,这个方法将在第一次有可能写入数据时被容器调用。仅当在ServletOutputStream上的isReady方法(描述如下)返回值为false之后容器才会调用onWritePossible方法,写操作才有可能实现。
    • onError(Throwable t)当处理响应发生错误时调用。
  • ServletOutputStream
    • boolean isReady() 如果对ServletOutputStream将成功,则此方法返回true,否则将返回false。如果该方法返回true,则可以在ServletOutputStream上执行写操作。如果没有进一步的数据可以写入ServletOutputStream,那么这个方法将返回false,直到底层数据被刷新,此时容器将调用WriteListener的onwriteable方法。随后对该方法的调用将返回true。
    • void setWriteListener(WriteListener listener).
      将WriteListener与这个ServletOutputStream关联起来。对于容器,当可以写入数据时,调用WriteListener上的回调方法。注册一个WriteListener将启动非阻塞IO。在那个时候切换到传统的阻塞IO是违法的。在此非法切换到传统的阻塞IO之后,使用IO相关的方法调用会产生不确定的行为。

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

5.便利方法

以下的便利方法存在于HttpServletResponse接口中:

  • sendRedirect
  • sendError

sendRedirect方法将设置适当的头和内容主体将客户机重定向到不同的URL。通过相对URL路径调用此方法是合法的,但是底层容器必须将相对路径转换为完全合格的URL,以便将其传输回客户机。不管出于什么原因,如果部分URL不能转化为有效的URL,那么这个方法必须抛出IllegalArgumentException。

sendError方法将为错误消息设置适当的头和内容体,以返回给客户机。可以向sendError方法提供一个可选的字符串参数,该方法可用于错误的内容体中。

这些方法将具有提交响应的副作用,如果它还没有提交,就终止它。在调用这些方法之后,servlet不应该对客户机进行进一步的输出。如果在调用这些方法之后,将数据写入响应,则忽略数据。

如果将数据写入响应缓冲区,但不返回给客户机(即响应未提交),则必须清除响应缓冲区中的数据,并使用这些方法所设置的数据替换。如果响应被提交,这些方法必须抛出一个IllegalStateException。

6. 国际化

servlet应该设置响应的语言环境和字符编码。区域设置使用ServletResponse.setLocale方法。该方法可重复调用;但在做出回应后调用是无效的。如果servlet在提交页面之前没有设置语言环境,则使用容器的默认语言环境来确定响应的语言环境,但是没有为与客户机的通信(比如HTTP中的Content-Language头)进行规范。

<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list

The <response-character-encoding> element can be used to explicitly set the default encoding for all responses in a given web application.

<response-character-encoding>UTF-8</response-character-encoding>

如果两个元素都不存在或不提供映射,则setLocale使用一个容器依赖映射。可以反复调用setCharacterEncoding、setContentType和setLocale方法来更改字符编码。在调用servlet响应的getWriter方法后调用,或在响应提交后调用,对字符编码没有影响。只有当给定的内容类型字符串为charset属性提供值时,对setContentType的调用才会设置字符编码。调用setLocale设置了字符编码仅当setcharacter编码或setContentType在之前没有设置字符编码。

如果servlet在调用ServletResponse接口的getWriter方法之前没有指定字符编码,或者响应已提交,则使用默认的ISO-8859-1。

如果使用的协议提供了这样做的方法,容器必须与客户端的语言环境和用于servlet响应的写入器的字符编码通信。在HTTP的情况下,语言环境通过Content-Language头进行通信,字符编码作为文本媒体类型的内容类型头的一部分。请注意,如果servlet没有指定内容类型,则无法通过HTTP报头来传递字符编码;然而,它仍然被用来编码通过servlet响应的writer写的文本。

7.响应对象的关闭

当响应被关闭时,容器必须立即将响应缓冲区中的所有剩余内容刷新到客户端。以下事件表明servlet满足了请求,响应对象将被关闭:

  • servlet的service方法的结束。
  • setContentLength中指定数量的内容或setContentLengthLong方法回应是大于零的且已经被写入响应。
  • sendError方法被调用。
  • sendRedirect方法被调用。
  • AsyncContext的complete方法被调用。

8.响应对象的生存期

每个响应对象只在servlet的service方法的范围内有效,或者在过滤器的doFilter方法的范围内有效,除非关联的请求对象为组件启用了异步处理。如果启动了相关请求的异步处理,则响应对象在调用AsyncContext的complete方法之前仍然有效。容器通常会回收响应对象,以避免响应对象创建的性能开销。开发人员必须意识到,在上述范围之外维护对尚未调用相应请求上的startAsync的响应对象的引用可能会导致非确定性行为。

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,174评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 一只蚂蚁一觉醒来,发现自己正躺在一片雪地里。 “哗,下雪了啊。”蚂蚁感叹着,伸了个懒腰。 “咦,不对啊,怎么一点也...
    椬yi阅读 645评论 0 0
  • 案例一 长1.40m 宽0.72m 高84cm厚度3cm椅背高度62cm 地点:山东大学 感受:适合仰坐 曲线符合...
    RobinSalt阅读 2,125评论 0 2