如何正确地向客户端发送 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 消息的主要内容,如果未能解决你的问题,请参考以下文章

php是如何实现websocket实时消息推送的

SpringBoot - WebSocket

SocketAsyncEventArgs 和缓冲,而消息是在部分

[基础]同步消息和异步消息传递的区别?

linux下,socket服务器和客户端TCP方式建立了连接,如何使它们之间相互发送消息?

C# - Websocket - 将消息发送回客户端