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)当处理响应发生错误时调用。
- void onWritePossible()
- 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的响应对象的引用可能会导致非确定性行为。