Setting both error message and body from servlet

2019-04-06 12:03发布

问题:

I want to write a servlet that will return an http response like so:

HTTP/1.1 500 <short custom message>
Content-Length: ...

<longer custom message>

The reason is that I want a programmatic client to be able to process the response message to take a particular response but I also want to fill in the response body with a longer explanation so that it's easy to hit using a browser.

Now, HttpServletResponse has a sendError(int, String) method that allows me to specify the error code and a message. The javadocs only say that the message will be embedded in some kind of html page, but nothing about setting the http response message. After calling this method, you are not allowed to write anything else to the response. In my tests (with jetty), the message is used both for the http response and an html body, which would be fine with me, except that I want to specify two different strings and I don't think the setting of the http response message is guaranteed with a different implementation.

There's also a setStatus(int) method that you can call with any code, and then you can write your own html body. This is close, except that you can't specify the http response message.

Finally, there's a setStatus(int, String) method that actually does exactly what I want, but it's deprecated due to some kind of ambiguity. I'm assuming some servlet containers were writing the message to the response body and closing the response.

Aside from using the deprecated method, I'm assuming I'm screwed here, but I'm curious if anyone else knows any tricks?

回答1:

There's also a setStatus(int) method that you can call with any code, and then you can write your own html body. This is close, except that you can't specify the http response message.

Is there any reason why you would not use response.setStatus(500) to set the HTTP status code, followed by writing to the response's output stream?

This is how "writing to the response body" is achieved at the lowest level.

Here's an example:

public class ErrorServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        // 500 error
        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        resp.getWriter().print("<html><head><title>Oops an error happened!</title></head>");
        resp.getWriter().print("<body>Something bad happened uh-oh!</body>");
        resp.getWriter().println("</html>");
    }
}

web.xml:

<web-app>
    <servlet>
        <servlet-name>myerror</servlet-name>
        <servlet-class>brown.testservlet.ErrorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>myerror</servlet-name>
        <url-pattern>/myErrorPage</url-pattern>
    </servlet-mapping>
</web-app>

And the output:

$ curl -I http://localhost:8080/testservlet/myErrorPage  
HTTP/1.1 500 Internal Server Error  
Content-Length: 108  
Server: Jetty(6.1.16)  

$ curl -v http://localhost:8080/testservlet/myErrorPage
* About to connect() to localhost port 8080 (#0)
*   Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /testservlet/myErrorPage HTTP/1.1
> User-Agent: curl/7.20.0 (i686-pc-mingw32) libcurl/7.20.0 OpenSSL/0.9.8k zlib/1.2.3
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Content-Length: 108
< Server: Jetty(6.1.16)
<
<html><head><title>Oops an error happened!</title></head><body>Something bad happened uh-oh!</body></html>
* Connection #0 to host localhost left intact
* Closing connection #0


回答2:

You can customize error pages by specifying error specific pages in web.xml:

<error-page>
  <error-code>500</error-code>
  <location>/error500.jsp</location>
</error-page>

In the jsp file you can use request attributes to show a custom message and other info.



回答3:

Well, sorry, the most obvious reason to have a body for an error response is to show something human-readable in a browser when an error happens. This is the reason why generating HTML is the default behaviour of sendError in the Servlet spec.

In my case, I want something different, because I'm creating web services, and this concrete one does not return a response if everything works fine.



回答4:

Using JBoss 6 I came to the conclusion that the response code must be set after you call getWriter to disable the Jboss custom error page response. I guess the implementation provides different writers depending on the status of the response.

response.getWriter().print(message);
response.setStatus(500);


回答5:

Seems like all the APIs designed for setting the reason phrase in HTTP are getting obsolete because of this:

RFC 7230, Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing, June 2014. Section 3.1.2:

The reason-phrase element exists for the sole purpose of providing a textual description associated with the numeric status code, mostly out of deference to earlier Internet application protocols that were more frequently used with interactive text clients. A client SHOULD ignore the reason-phrase content.

Tomcat removed support for org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER property in version 8.5.0.



回答6:

If you want to have a custom error message in the headers why not add you own header with response.addHeader(String, String)? Combining this with matt b's answer could do the job.