The Response 响应
响应对象包装了从服务器端返回到客户端的所有信息。在HTTP协议上,这些信息既可以通过HTTP headers 又可以通过响应体从服务器端传输到客户端。
5.1 缓冲
为了效率,servlet 容器允许但非必须缓冲到客户端的输出。典型地,服务器默认使用缓冲,但是允许 servlets 可以指明缓冲参数。
ServletResponse 接口中的下列方法允许 servlet 访问和设置缓冲信息:
■ getBufferSize
■ setBufferSize
■ isCommitted
■ reset
■ resetBuffer
■ flushBuffer
ServletResponse 接口提供的这些方法允许执行缓冲操作,无论 servlet 使用 ServletOutputStream 还是 Writer。
getBufferSize 方法返回被隐含使用的缓冲的大小。如果没有缓冲被使用,这个方法返回整数值0。
servlet 可以使用 setBufferSize 方法请求一个最佳的缓冲大小。赋予的缓冲不必是 servlet 请求的大小,但是必须至少与与请求的大小一样大。这样允许容器重用一组固定大小的缓冲,如何合适的话,提供一个比请求更大的缓冲。该方法必须在使用 ServletOutputStream 或 Writer 写入任何内容前调用。如果任何内容已经被写入或响应对象已经被提交,此方法必须抛出一个IllegalStateException。
isCommitted 方法返回一个布尔值表示所有(任何)响应字节已经返回到客户端。
flushBuffer 方法强制缓冲中的内容被写到客户端。
当响应没有被提交时,reset 方法清除缓冲中的数据。Headers,status codes 和 调用 reset 之前调用 getWriter 或 getOutputStream 被 servlet 设置的状态也被清除。如果响应没有被提交,resetBuffer 方法清除缓冲中的内容,但是不清除 headers 和status code。
如果响应已经被提交,调用 reset 或者 resetBuffer 方法,必须抛出IllegalStateException。响应和它关联的缓冲将不会改变。
当使用缓冲时,容器必须立即将填满缓冲的内容刷出到客户端 。如果这是发送到客户端的第一(唯一?)数据,可认为响应已经被提交。
5.2 Headers 头
servlet 可以通过 HttpServletResponse 接口的以下方法设置 HTTP 响应的头:
■ setHeader
■ addHeader
setHeader方法使用一个给定的名称和值设置一个 header。之前的 header 被新 header 替换。当此名有一个 header 值集合,所有值被清除并用新值代替。
addHeader 方法添加一个 header 值到给定名称的头的值集。如果该名称没有关联的头,一个新集被创建。Headers 可以包含表示 int 或 Date 对象的数据。HttpServletResponse 接口的下列便捷方法允许 servlet 为合适的数据类型使用正确的格式设置 header:
■ setIntHeader
■ setDateHeader
■ addIntHeader
■ addDateHeader
为了成功的传回客户端,头必须在响应提交之前设置。响应提交之后设置的头将被 servlet 容器忽略。
Servlet 开发者有责任保证为 servlet 生成的内容在响应对象中设置合适的 Content-type 头。HTTP 1.1 规范没有要求 HTTP 响应中设置此头。开发者没有设置此类型时,servlet 容器不必设置一个默认的内容类型(content type)。
建议容器使用 X-Powered-By HTTP header 发布它的实现信息。这个字段的值应该由一个或者多个实现类型组成,比如:”Servlet/3.1”。可选的,容器的补充信息和隐含的Java平台信息可以添加在实现类型后面的括号内。容器应可配置隐藏此 header。
以下是此 header 的例子:
X-Powered-By: Servlet/3.1
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
5.3 Non Blocking IO 非阻塞IO
非阻塞IO只有在 Servlets 和 Filters 的异步请求处理和升级处理时才有效。否则当调用 ServletInputStream.setReadListener 或 ServletOutputStream.setWriteListener 时必须抛出IllegalStateException。为了支持在Web容器中非阻塞写,除了在3.7节中描述的 ServletRequest 的变化之外,与处理响应有关的类/接口也发生了下面的变化:
WriteListener 提供了适合容器调用的如下回调方法:
■ WriteListener
■ void onWritePossible(). 当一个 WriteListener 注册到 ServletOutputStream 时,当可以写数据时这个方法被容器第一次调用。当且仅当下面描述的 ServletOutputStream 的 isReady 方法返回false 时,容器接下来会调用 onWritePossible 方法。
■ onError(Throwable t). 当处理响应发生错误时被调用。
与WriteListener一起,下面添加到 ServletOutputStream 类的方法允许开发者在运行时检查是否有发送到客户端的可写数据。
■ ServletOutputStream
- boolean isReady(). 如果对 ServletOutputStream 的写会成功,该方法返回true,否则返回 false。如果该方法返回 true,可以在 ServletOutputStream 上执行一个写操作。如果没有后续数据可以写到 ServletOutputStream,那么直到容器调用 WriteListener 的 onWritePossible 方法将隐含的数据刷出之前,此方法将返回false。该方法的后续调用将会返回true。
- void setWriteListener(WriteListener listener). 用 WriteListener 关联 ServletOutputStream。当 ServletOutputStream 可写数据时,容器调用 WriteListener 上的回调方法。注册一个 WriteListener 将开启非阻塞IO。此时切换到传统阻塞IO是非法的。Servlet 容器必须用线程安全的方式访问 WriteListener 的方法。
5.4 Convenience Methods 便捷方法
HttpServletResponse 接口中存在以下便捷方法:
■ sendRedirect
■ sendError
sendRedirect方法将设置合适的头和内容体重定向客户端到一个不同的URL。调用这个方法时使用一个相对URL路径是合法的,但是隐含的容器必须转换这个相对路径为一个全路径URL传回客户端。如果给了一个不完整的URL,不管什么原因都不能转换为一个有效的URL,此方法必须抛出一个IllegalArgumentException。
sendError方法将为错误信息设置合适的头和内容体返回到客户端。sendError方法支持一个可选的字符串参数用来设置错误的内容体。
这些方法会对提交中的响应产生副作用,如果响应还没有被提交,将会中断它(?)。这些方法被调用之后,servlet 不会产生到客户端的后续输出。如果这些方法被调用后,有数据写到了响应,这些数据将被忽略。如果数据被写到了响应缓冲,但是还没有返回到客户端(比如:响应还没有比提交),响应缓冲中的数据必须被清除,并且用这些方法设置的数据来替代。如果响应被提交,这些方法必须抛出一个IllegalStateException。
5.5 Internationalization 国际化
Servlets 应该设置 locale 和响应的字符编码。locale 使用 ServletResponse.setLocale 方法设置。该方法可以重复调用;但是响应提交后的调用没有效果。如果页面提交前 servlet 没有设置 locale,使用容器的默认 locale 来确定响应的 locale,但是没有为与客户端的通信制定规范,比如使用 HTTP 情况下的Content-Language 头。
<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list>
如果元素不存在或者没有提供一个映射,setLocale 使用容器依赖的映射。setCharacterEncoding ,setContentType和 setLocale 方法可以被重复调用来改变字符编码。在 servlet 响应的 getWriter 方法被调用后或响应被提交后的调用不会对字符编码产生影响。仅在用给定的内容类型字符串为 charset 属性提供一个值的时候,调用 setContentType 来设置字符编码。只有当既没有使用 setCharacterEncoding 又没有使用 setContentType 设置字符编码之前调用 setLocale 设置字符编码(是不是应该设置Locale?)。
如果调用 ServletResponse 接口的 getWriter 方法或响应提交之前,servlet 没有指定字符编码,将默认使用“ISO-8859-1”。
如果使用的协议提供了这样的方法,容器必须将 servlet 响应的 wirter 使用的 locale 和字符编码传给客户端。在使用HTTP时,通过 Content-Language 头传递 locale,字符编码作为文本媒体类型 Content-Type 头的一部分传递。注意:如果 servlet 没有指明内容类型(content type),字符编码不能通过HTTP 头传递,但是仍然可以使用 servlet 响应的 writer 对文本编码。
5.6 Closure of Response Object 响应对象的关闭
当响应关闭时,容器必须立即将响应缓冲中剩余的内容刷出到客户端。下面的事件表明 servlet 满足了请求,响应对象将被关闭:
- servlet 的 service 方法结束。
- 在 response 的 setContentLength 或 setContentLengthLong 方法中指定的内容量大于 0,并且已经写入响应。
- sendError 方法被调用。
- sendRedirect 方法被调用。
- AsyncContext 的 complete 方法被调用。
5.7 Lifetime of the Response Object 响应对象的生命周期
除非关联的请求对象为组件开启了异步处理,否则每个响应对象只在 servlet 的 service 方法或 filter 的 doFilter 方法的作用域中有效。如果相关联请求的异步处理已经开始,在 AsyncContext 的 complete 方法调用之前该请求对象一直有效。为了避免创建响应对象的性能开销,容器通常会回收利用响应对象。开发者必须注意相应的请求没有被调用 startAsync 方法,在上面描述的作用域之外保持响应对象的引用可能会导致无法确定的行为。