如何正确地向客户端发送 HTTP 消息
Posted
技术标签:
【中文标题】如何正确地向客户端发送 HTTP 消息【英文标题】:How to properly send an HTTP message to the client 【发布时间】:2010-11-09 05:00:40 【问题描述】:我正在使用 Java 开发 RESTful Web 服务。如果出现问题,我需要一种向客户端发送错误消息的好方法。
根据Javadoc,HttpServletResponse.setStatus(int status, String message)
已弃用“由于消息参数的含义不明确。”
是否有设置响应的状态消息或“reason phrase”的首选方法? sendError(int, String)
方法不行。
编辑:
澄清一下,我想修改 HTTP 状态行,即"HTTP/1.1 404 Not Found"
,而不是正文内容。具体来说,我想发送像"HTTP/1.1 400 Missing customerNumber parameter"
这样的回复。
【问题讨论】:
使用 sendError 时 servlet 容器返回的默认原因短语是否有问题? 没有什么特别的问题,只是我想发送一个更具体的信息。 【参考方案1】:我认为任何 RESTful 客户端都不会期望通过查看原因短语来找出问题所在;我见过/使用的大多数 RESTful 服务将在响应正文中发送标准状态信息和扩展消息。 sendError(int, String)
非常适合这种情况。
【讨论】:
一些人争辩说我什至不应该通过原因短语发送消息,我想我相信了。但是,我不能使用 sendError(int, String),因为容器会破坏消息。如果我调用 sendError(400, "blah"),响应正文(至少在 WebSphere 中)是“错误 400:blah”。不好,特别是如果你想返回 XML 或其他一些严格的格式。我将调用 setStatus(int) 并手动编写正文内容。 来自 *HeadersFilter 的 doFilterInternal 使用 spring-boot 2 配置,response.sendError(errorCode, errorMessage) 无法正常工作。 1.第一个问题是总是抛出404,要解决这个问题我们必须添加setRegisterErrorPageFilter(false);在配置方法中。 2.第二次发布消息格式,它看起来像一个html格式的服务器错误页面,根据Javadoc for sendError将内容类型设置为“text/html”。所以更好的方法是使用 response.setStatus(errorCode) 和 resp.getWriter().print(errorMessage)。花了一些时间来解决这个问题!希望有帮助!【参考方案2】:如果您使用的是 Tomcat,请参阅设置 org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER:
http://tomcat.apache.org/tomcat-5.5-doc/config/systemprops.html
如果是这样,自定义 HTTP 状态消息将在 HTTP 标头中使用。用户必须确保任何此类消息都经过 ISO-8859-1 编码,特别是如果消息中包含用户提供的输入,以防止可能的 XSS 漏洞。如果未指定,将使用默认值 false。有关原始漏洞的一些详细信息,请参阅此页面:
http://www.securityfocus.com/archive/1/archive/1/495021/100/0/threaded
【讨论】:
注意,从 tomcat 8.5 开始不再支持上述系统属性。相反,现在有一个连接器选项“sendReasonPhrase”——tomcat 9 也将弃用该选项,以符合最新的 HTTP 规范,该规范排除了原因短语的使用。另请参阅此处的讨论:bz.apache.org/bugzilla/show_bug.cgi?id=60362【参考方案3】:在您澄清之后,我在 Tomcat 中尝试了这个。执行中
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "message goes here");
返回
HTTP/1.1 400 message goes here
作为响应的第一行。
你使用的servlet容器一定有问题。
【讨论】:
根据文档,sendError 只保证发送内容中的消息。它没有说明状态行。 嗯,我不记得 sendError 设置了任何内容?我一直认为如果应用程序不提供 Web 服务器部分会生成一些默认内容。您提供的链接似乎也没有提到这一点? 我试过了,这种设置响应体的方式,结果是HTML体,虽然里面包含了我设置的responseBody。【参考方案4】:我不太熟悉 REST 的“最佳实践”。但我知道这个概念是基于 HTTP 的,并且它应该如何自然地发挥作用。那么如何在正文中使用 mime 类型和简单文本来处理应用程序错误,例如“application/myapp-exception”和一些“Bla bla”?您可以为此提供一个客户端库。
我不会对应用程序错误使用 HTTP 响应代码。因为我想知道什么是失败的:是我的应用程序还是我的 HTTP 服务器。
(我希望,我也会在这里看到一些最佳实践建议。)
【讨论】:
在这种情况下,403 Forbidden 是否表示失败的 HTTP 服务器或失败的应用程序?那么ietf.org/rfc/rfc2324.txt 中定义的 418 I'm a teapot 又如何呢? ;-) 呵呵,说得好。但是我提出了这个解决方案,因为标题内容是有限的。所以是的,我必须纠正自己。使用响应代码来表示应用程序/服务器错误是正确的。但由于限制(编码、长度等),我不会发送带有标题的错误消息。对于发送错误消息本身,我建议使用 MIME 类型和响应正文。因此,在为客户端创建错误消息时,您的应用程序不必知道任何限制。【参考方案5】:目前还不清楚您要完成什么。我的第一个想法是 sendError 但你说它没有做你想要的......你是否看过创建一组“错误响应”,这意味着特定的 xml 或 JSON 内容(或任何你用作传输语言的内容)包含错误消息或代码以及任何其他有用的信息?
不久前,我为基于 Spring-mvc 的 RESTful 服务做了类似的事情,它运行良好,但您必须捕获并处理每个异常,以防止客户端收到通用的 500 消息或其他东西。 Spring 异常解析器在这方面工作得很好。
希望这会有所帮助...如果没有,也许可以更清楚地说明您要完成的工作。对不起,如果我很密集并且遗漏了一些明显的东西。
【讨论】:
【参考方案6】:我认为sendError
应该这样做,但您的应用程序服务器可能出现故障... IBM WebSphere 3.5 很久以前就在我身上失败了,而 Tomcat 可以很好地传播消息;请参阅 Sun 论坛上的 JavaServer Pages (JSP) and JSTL - Error page: preserve header "HTTP/1.x 400 My message"?。
最终我使用了以下解决方法,但这是特定于 JSP 的,实际上可能是旧的:
<%@ page isErrorPage="true" %>
<%
// This attribute is NOT set when calling HttpResponse#setStatus and then
// explicitely incuding this error page using RequestDispatcher#include()
// So: only set by HttpResponse#sendError()
Integer origStatus =
(Integer)request.getAttribute("javax.servlet.error.status_code");
if(origStatus != null)
String origMessage =
(String)request.getAttribute("javax.servlet.error.message");
if(origMessage != null)
response.reset();
response.setContentType("text/html");
// deprecated, but works:
response.setStatus(origStatus.intValue(), origMessage);
// would yield recursive error:
// response.sendError(origStatus, origMessage);
%>
如果您碰巧使用 Internet Explorer 进行测试:禁用“显示友好的 HTTP 错误消息”。 (当不禁用它时,IE 对 HTML 内容的一些最小长度有一些奇怪的要求,如果不满足,将 - 或将 - 使 IE 显示它自己的错误消息。另请参见 Microsoft 的 @ 注册表键 HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\ErrorThresholds
987654322@.)
【讨论】:
【参考方案7】:在 Spring 驱动的 Web 应用程序中,在 Tomcat 上运行我使用以下 bean:
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.springframework.beans.factory.InitializingBean;
public class SystemPropertiesInitializingBean implements InitializingBean
private Map<String, String> systemProperties;
@Override
public void afterPropertiesSet() throws Exception
if (null == systemProperties || systemProperties.isEmpty())
return;
final Set<Entry<String, String>> entrySet = systemProperties.entrySet();
for (final Entry<String, String> entry : entrySet)
final String key = entry.getKey();
final String value = entry.getValue();
System.setProperty(key, value);
public void setSystemProperties(final Map<String, String> systemProperties)
this.systemProperties = systemProperties;
在applicationContext.xml中:
<bean class="....SystemPropertiesInitializingBean">
<property name="systemProperties">
<map>
<entry key="org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER" value="true"/>
</map>
</property>
</bean>
【讨论】:
以上是关于如何正确地向客户端发送 HTTP 消息的主要内容,如果未能解决你的问题,请参考以下文章
SocketAsyncEventArgs 和缓冲,而消息是在部分