20200311 5. Response
5. Response
响应( response)对象封装了从服务器返回到客户端的所有信息。在 HTTP 协议中,从服务器传输到客户端
的信息通过 HTTP 头信息或响应的消息体。
5.1 缓冲
Servlet 容器允许但不必为了提高效率而缓冲到客户端的输出。典型的服务器默认都是缓冲的,但允许 servlet
指定缓冲参数。
ServletResponse 接口的如下方法允许 servlet 访问和设置缓冲信息:
getBufferSizesetBufferSizeisCommittedresetresetBufferflushBuffer
不管 Servlet 使用的是一个 ServletOutputStream 还是一个 Writer, ServletResponse 接口提供的这些方法允许执行缓冲操作。
getBufferSize 方法返回使用的底层缓冲区大小。如果没有使用缓冲,该方法必须返回一个 int 值 0。
Servlet 可以请求 setBufferSize 方法设置一个最佳的缓冲大小。不一定分配 servlet 请求大小的缓冲区,但至
少与请求的大小一样大。这允许容器重用一组固定大小的缓冲区,如果合适,可以提供一个比请求时更大的缓冲区。该方法必须在使用 ServletOutputStream 或 Writer 写任何内容之前调用。如果已经写了内容或响应对象已经提交,则该方法必须抛出 IllegalStateException。
isCommitted 方法返回一个表示是否有任何响应字节已经返回到客户端的 boolean 值。 flushBuffer 方法强制
刷出缓冲区的内容到客户端。
当响应没有提交时, reset 方法清空缓冲区的数据。头信息,状态码和在调用 reset 之前 servlet 调用 getWriter或 getOutputStream 设置的状态也必须被清空。如果响应没有被提交, resetBuffer 方法将清空缓冲区中的内容,但不清空请求头和状态码。
如果响应已经提交并且 reset 或 resetBuffer 方法已被调用,则必须抛出 IllegalStateException,响应及它关联的缓冲区将保持不变。
当使用缓冲区时,容器必须立即刷出填满的缓冲区内容到客户端。如果这是最早发送到客户端的数据,且认为响应被提交了。
5.2 头信息
Servlet 可以使用如下 HttpServletResponse 接口中的方法设置 HTTP 响应头:
setHeaderaddHeader
setHeader 方法设置一个给定名字和值的 header。之前的 header 将被新的 header 替换。如果已经存在同名的
header 值的 set, set 中的值会被清空并用新的值替换。
addHeader 方法使用给定的名字添加一个 header 值到 set。如果没有 header 与给定的名字关联,则创建一个
新的 set。
Header 可能包含表示 int 或 Date 对象的数据。以下 HttpServletResponse 接口提供的便利方法允许 Servlet 对适当的数据类型用正确的格式设置一个 header:
setIntHeadersetDateHeaderaddIntHeaderaddDateHeader
为了成功的传回给客户端, header 必须在响应提交前设置。响应提交后的 Header 设置将被 servlet 容器忽略。
Servlet 程序员负责保证为 Servlet 生成的内容设置合适的 response 对象的 Content-Type header。 HTTP 1.1 规范中没有要求在 HTTP 响应中设置此 header。当 Servlet 程序员没有设置该类型时, Servlet 容器也不能设置
默认的内容类型。
建议容器使用 X-Powered-By HTTP header 公布它的实现信息。该字段值应考虑一个或多个实现类型,如
"Servlet/3.1"。容器应该可以配置来隐藏该 header。可选的容器补充的信息和底层 Java 平台可以被放在括号
内并添加到实现类型之后。
X-Powered-By: Servlet/3.1
X-Powered-By: Servlet/3.1 JSP/2.2 (GlassFish Open Source Edition 4.0 Java/Oracle Corporation/1.7)
5.3 非阻塞 IO
非阻塞 IO 仅对在 Servlet 和 Filter(2.3.3.3 节定义的, “异步处理”)中的异步请求处理和升级处理
(2.3.3.5 节定义的,“升级处理”)有效。否则,当调用 ServletInputStream.setReadListener 或
ServletOutputStream.setWriteListener 方法时将抛出 IllegalStateException。 为了支持在 Servlet 容器中的非阻塞写,除了在 3-28 页 3.7 节描述的“非阻塞 IO” 对 ServletRequest 做的更改之外,下面做出的更
改以便于处理响应相关的类/接口。
WriteListener 提供了如下适用于容器调用的回调方法。
-
void onWritePossible(). 当一个WriteListener注册到ServletOutputStream时,当可以写数据时该方法将被容器首次调用。当且仅当下边描述的ServletOutputStream的isReady方法返回false,容器随后将调用该方法。 -
onError(Throwable t). 当处理响应过程中出现错误时回调。
除了 WriteListener 外,还有如下方法被添加到 ServletOutputStream 类并允许开发人员运行时检查是否可以写数据发送到客户端。
-
boolean isReady(). 如果往ServletOutputStream写会成功,则该方法返回true,其他情况会返回false。如果该方法返回true,可以在ServletOutputStream上执行写操作。如果没有后续的数据能写到
ServletOutputStream,那么直到底层的数据被刷出之前该方法将一直返回false。且在此时容器将调用
WriteListener的onWritePossible方法。随后调用该方法将返回true。 -
void setWriteListener(WriteListener listener). 关联WriteListener和当前的ServletOutputStream,当ServletOutputStream可以写入数据时容器会调用WriteListener的回调方法。注册了WriteListener将开始非阻塞 IO。此时再切换到传统的阻塞 IO 是非法的。
容器必须线程安全的访问 WriteListener 中的方法。
5.4 简便方法
HttpServletResponse 提供了如下简便方法:
sendRedirectsendError
sendRedirect 方法将设置适当的 header 和内容体将客户端重定向到另一个地址。使用相对 URL 路径调用该
方法是合法的,但是底层的容器必须将传回到客户端的相对地址转换为全路径 URL。无论出于什么原因,如 果 给 定 的 URL 是 不 完 整 的 , 且 不 能 转 换 为 一 个 有 效 的 URL , 那 么 该 方 法 必 须 抛 出
IllegalArgumentException。
sendError 方法将设置适当的 header 和内容体用于返回给客户端返回错误消息。可以 sendError 方法提供一
个可选的 String 参数用于指定错误的内容体。
如果响应已经提交并终止,这两个方法将对提交的响应产生负作用。这两个方法调用后 servlet 将不会产生
到客户端的后续的输出。这两个方法调用后如果有数据继续写到响应,这些数据被忽略。 如果数据已经写
到响应的缓冲区,但没有返回到客户端(例如,响应没有提交),则响应缓冲区中的数据必须被清空并使用
这两个方法设置的数据替换。如果想要已提交,这两个方法必须抛出 IllegalStateException。
5.5 国际化
Servlet 应设置 response 的 locale 和字符集。使用 ServletResponse.setLocale 方法设置 locale。该方法可以重复的调用;但响应被提交后调用该方法不会产生任何作用。如果在页面被提交之前 Servlet 没有设置 locale,
容器的默认 locale 将用来确定响应的 locale,但是没有制定与客户端通信的规范,例如使用 HTTP 情况下的
Content-Language header。
<locale-encoding-mapping-list>
<locale-encoding-mapping>
<locale>ja</locale>
<encoding>Shift_JIS</encoding>
</locale-encoding-mapping>
</locale-encoding-mapping-list>
如果该元素不存在或没有提供 mapping, setLocale 使用容器依赖的 mapping。 setCharacterEncoding,
setContentType 和 setLocale 方法可以被重复的调用来改变字符编码。如果在 servlet 响应的 getWriter 方法已经调用之后或响应被提交之后,调用相关方法设置字符编码将没有任何作用。只有当给定的 content type 字
符 串 提 供 了 一 个 charset 属 性 值 , 调 用 setContentType 可 以 设 置 字 符 编 码 。 只 有 当 既 没 有 调 用
setCharacterEncoding 也没有调用 setContentType 去设置字符编码之前调用 setLocale 可以设置字符编码。
在 ServletResponse 接口的 getWriter 方法被调用或响应被提交之前,如果 servlet 没有指定字符编码,默认使用 ISO-8859-1。
如果使用的协议提供了一种这样做的方式,容器必须传递 servlet 响应的 writer 使用的 locale 和字符编码到
客户端。使用 HTTP 的情况下, locale 可以使用 Content-Language header 传递,字符编码可以作为用于指定
文本媒体类型的 Content-Type header 的一部分传递。注意,如果没有指定 content type,字符编码不能通过
HTTP header 传递;但是仍使用它来编码通过 servlet 响应的 writer 写的文本。
5.6 结束响应对象
当响应被关闭时,容器必须立即刷出响应缓冲区中的所有剩余的内容到客户端。
以下事件表明 servlet 满足了请求且响应对象即将关闭:
- servlet 的
service方法终止。 - 响应的
setContentLength或setContentLengthLong方法指定了大于零的内容量,且已经写入到响应。 sendError方法已调用。sendRedirect方法已调用。AsyncContext的complete方法已调用。
5.7 Response 的生命周期
每个响应对象是只有当在 servlet 的 service 方法的范围内或在 filter 的 doFilter 方法范围内是有效的,除非该组件关联的请求对象已经开启异步处理。如果相关的请求已经启动异步处理,那么直到 AsyncContext 的
complete 方法被调用,请求对象一直有效。为了避免响应对象创建的性能开销,容器通常回收响应对象。
在相关的请求的 startAsync 还没有调用时,开发人员必须意识到保持到响应对象引用,超出之上描述的范
围可能导致不确定的行为。
浙公网安备 33010602011771号