50

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".

Or I want to modify the HTTP status line to say something like 227 IM Used - in other words: an HTTP Status Description different from what the web-server would know to send.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Adam Crume
  • 15,614
  • 8
  • 46
  • 50
  • Is there something wrong with the default reason phrase that your servlet container returns when you use sendError? – laz Jul 08 '09 at 22:49
  • 2
    There's nothing particularly wrong with it, just that I want to send a more specific message. – Adam Crume Jul 08 '09 at 23:28
  • @lax One issue with using `sendError` is noted in the documentation: *"The server ... may clear or update any headers needed to serve the error page as a valid response. If an error-page declaration has been made for the web application corresponding to the status code passed in, it will be served back the error page If the response has already been committed, this method throws an IllegalStateException."* I want to send back `226 IM Used`, or any other status code and description i choose, and **not** send an error. – Ian Boyd Aug 02 '22 at 20:07

7 Answers7

25

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
  • 70,339
  • 36
  • 160
  • 222
  • 6
    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 Jul 09 '09 at 14:58
  • 1
    From doFilterInternal of *HeadersFilter With spring-boot 2 configuration, response.sendError(errorCode, errorMessage) doesn't work properly. 1. First issue is it is always throwing 404, to solve this we have to add setRegisterErrorPageFilter(false); in the configure method. 2. Second issue it the message format, it look like an HTML-formatted server error page, setting content type to "text/html" as per Javadoc for sendError. So better way would be using response.setStatus(errorCode) along with resp.getWriter().print(errorMessage). Took some time to figure this out! hope it helps! – Kusum Apr 29 '20 at 17:15
20

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.securityfocus.com/archive/1/archive/1/495021/100/0/threaded

Nicktar
  • 5,548
  • 1
  • 28
  • 43
ars
  • 120,335
  • 23
  • 147
  • 134
  • Note, the above system property is not anymore supported beginning with tomcat 8.5. Instead there is now a connector option "sendReasonPhrase" - and that too will be deprecated with tomcat 9 in favor of compliance with the latest HTTP spec which excludes the use of a reason phrase. Also see discussion here: https://bz.apache.org/bugzilla/show_bug.cgi?id=60362 – avallen Jun 14 '18 at 12:52
15

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
  • 28,320
  • 5
  • 53
  • 50
  • 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 Jul 08 '09 at 23:25
  • 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 Jul 08 '09 at 23:34
  • 1
    I tried, this way of setting the response body, but it turned out to be an HTML body, though it contains the responseBody which I had set. – Piyush Upadhyay Aug 02 '20 at 15:52
2

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.)

cafebabe
  • 1,400
  • 8
  • 9
  • 3
    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 Jul 08 '09 at 23:30
  • 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. – cafebabe Jul 09 '09 at 10:38
0

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
  • 13,468
  • 4
  • 44
  • 56
0

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
  • 22,808
  • 11
  • 61
  • 71
0

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>
sth
  • 222,467
  • 53
  • 283
  • 367
Illarion Kovalchuk
  • 5,774
  • 8
  • 42
  • 54