如何正确地向客户端发送HTTP消息

问题描述:

我正在使用Java中的REST风格的Web服务。如果出现问题,我需要一个很好的方法来向客户端发送错误消息。如何正确地向客户端发送HTTP消息

根据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"

+0

当您使用sendError时,servlet容器返回的默认原因短语是否有问题? – laz 2009-07-08 22:49:56

+0

它没有什么特别的错,只是我想发送一个更具体的消息。 – 2009-07-08 23:28:18

我不认为任何RESTful客户端会希望看一下原因短语找出什么地方出了错;我见过/使用过的大多数RESTful服务都会在响应的主体中发送标准状态信息和扩展消息。 sendError(int, String)非常适合这种情况。

这是不是很清楚你想要完成什么。我的第一个想法是sendError,但你说这不会做你想做的事......你是否看过创建一组“错误响应”,意思是特定的XML或JSON内容(或任何你用作传输语言的)包含错误消息或代码以及其他任何有用的信息?

我做了类似于基于Spring-mvc的RESTful服务,而且它运行良好,但是您必须捕获并处理每个异常,以防止客户端获得通用的500消息或其他东西。 Spring Exception Resolvers在这方面效果很好。

希望这有助于......如果没有,也许你想要完成什么更清晰一点。对不起,如果我正在密集和失去明显的东西。

我对REST的'最佳实践'还不是很熟悉。但我知道这个概念是基于HTTP以及它应该如何自然解决。那么如何在应用程序错误中使用MIME类型和简单文本,如'application/myapp-exception'和一些'Bla bla'?您可以为此提供一个客户端库。

我不会对应用程序错误使用HTTP响应代码。因为我想知道什么是失败的:无论是我的应用程序还是我的HTTP服务器。

(我希望,我会在这里看到一些最佳实践建议,也。)

+3

在这种情况下,* 403 Forbidden *表示失败的HTTP服务器还是失败的应用程序?那么* 418我是一个茶壶*,如http://www.ietf.org/rfc/rfc2324.txt中所定义的那样? ;-) – Arjan 2009-07-08 23:30:06

+0

嘿嘿,好点。但是我提出了这个解决方案,因为标题内容是有限的。所以是的,我必须纠正自己。使用响应代码来表示应用程序/服务器错误是正确的。但是由于限制(编码,长度等),我不会将错误信息与标题一起发送。为了发送错误消息本身,我建议使用MIME类型和响应主体。因此,在为客户端创建错误消息时,您的应用程序不必知道任何限制。 – cafebabe 2009-07-09 10:38:11

我认为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浏览器时,IE对HTML内容的某些最小长度有一些奇怪的要求,如果不符合,将会使IE显示自己的错误消息。另请参阅微软的Description of Hypertext Transport Protocol Error Messages上的注册表项HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\ErrorThresholds。)

经过澄清之后,我在Tomcat中试了这个。执行

response.sendError(HttpServletResponse.SC_BAD_REQUEST, "message goes here"); 

返回

HTTP/1.1 400 message goes here 

作为响应的第一行。

必须有与您正在使用的servlet容器有问题。

+0

根据文档,sendError只保证在内容中发送消息。它没有提到任何有关状态的信息。 – 2009-07-08 23:25:03

如果您正在使用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

在动力弹簧的Web应用程序,Tomcat上运行我用以下豆:

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>