views:

694

answers:

8

I'm working on a RESTful web service in Java. I need a good way to send error messages to the client if something's wrong.

According to the Javadoc, HttpServletResponse.setStatus(int status, String message) is deprecated "due to ambiguous meaning of the message parameter."

Is there a preferred way to set the status message or "reason phrase" of the response? The sendError(int, String) method doesn't do it.

EDIT: To clarify, I want to modify the HTTP status line, i.e. "HTTP/1.1 404 Not Found", not the body content. Specifically, I'd like to send responses like "HTTP/1.1 400 Missing customerNumber parameter".

A: 

It's not really clear what you are trying to accomplish. My first thought was the sendError but you say that does not do what you want... have you looked at creating a set of "error responses", meaning specific xml or JSON content (or whatever you are using as a transfer language) that contains the error message or code and any other useful information?

I did something like that for Spring-mvc based RESTful services a while back and it worked well but you have to pretty much catch and handle every exception to keep the client from getting a generic 500 message or something. The Spring Exception Resolvers worked well for that.

Hope this helps... if not, maybe a little more clarity on what you are trying to accomplish. Sorry if I am being dense and missing something obvious.

cjstehno
+1  A: 

I'm not quite familiar with the 'best practices' around REST. But I know the concept is based on HTTP and how it is supposed to work out naturally. So how about using a mime type and simple text inside the body for an application error, like 'application/myapp-exception' and some 'Bla bla'? You can provide a client library for that.

I would not use HTTP response codes for application errors. Because I like to know what's failing: whether it is my application or my HTTP server.

(I hope, I'll see some best practice advices here, too.)

oeogijjowefi
In that case, does *403 Forbidden* denote a failing HTTP Server or a failing application? And what about *418 I'm a teapot*, as defined in http://www.ietf.org/rfc/rfc2324.txt ? ;-)
Arjan
Hehe, good point. But I proposed this solution, because header contents are limited. So yes, I have to correct myself. It is right to use the response code for denoting an application/server error. But I would not send the error message with the header, because of the limitations (encoding, length etc). For sending the error message itself, I propose to use the MIME type and the response body. So your application does not have to know about any limitations, when creating the error message for the client.
oeogijjowefi
A: 

I think the sendError should do it, but your application server may be failing... IBM WebSphere 3.5 failed on me a long time ago while Tomcat would propagate the message just fine; see JavaServer Pages (JSP) and JSTL - Error page: preserve header "HTTP/1.x 400 My message"? on the Sun forums.

Eventually I used the following workaround, but this is kind of JSP specific, and may in fact be old:

<%@ 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); 
        }
    }
%>

And if you happen to test with Internet Explorer: disable "Show friendly HTTP error messages". (When not disabling that, IE has some odd requirement of some minimum length of the HTML content which, if not met, would —or will— make IE show its own error message instead. See also the registry key HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\ErrorThresholds at Microsoft's Description of Hypertext Transport Protocol Error Messages.)

Arjan
A: 

After your clarification, I tried this in Tomcat. Executing

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

returns

HTTP/1.1 400 message goes here

as the first line in the response.

There must be a problem with the servlet container you are using.

laz
According to the documentation, sendError is only guaranteed to send the message in the content. It doesn't say anything about the status line.
Adam Crume
Hmmm, I don't recall sendError setting any content? I've always thought that the web server part would generate some default content if the application didn't supply it. The link you provided doesn't seem to mention this either?
Arjan
+3  A: 

I don't think any RESTful client would expect to look at the reason phrase to figure out what went wrong; most RESTful services I've seen/used will send the standard status info and an expanded message in the body of the response. sendError(int, String) is ideal for that situation.

Hank Gay
A couple of people have argued that I shouldn't even be sending the message via the reason phrase, and I guess I'm convinced. I can't use sendError(int, String), though, because the container mangles the message. If I call sendError(400, "blah"), the response body (at least in WebSphere) is "Error 400: blah". Not good, especially if you want to return XML or some other strict format. I'll call setStatus(int) and write body content manually instead.
Adam Crume
+2  A: 

If you're using Tomcat, see the setting org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER:

http://tomcat.apache.org/tomcat-5.5-doc/config/systemprops.html

  • If this is true custom HTTP status messages will be used within HTTP headers. Users must ensure that any such message is ISO-8859-1 encoded, particularly if user provided input is included in the message, to prevent a possible XSS vulnerability. If not specified the default value of false will be used.

See this page for some detail on the original vulnerability:

http://www.f-secure.com/vulnerabilities/SA31379

ars
Aha, good to know!
Arjan
A: 

In Spring powered web application, running on Tomcat I use following 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;
    }

}

And in 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>
Shaman
A: 

Arjan,

Good point. I had same issue, and followed what you said about disable the "Show friendly HTTP error messages" in IE, and then IE instead of displaying its own message, displayed the message from my application that was sent using HttpServletResponse.sendError.

billy_cool