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.
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.
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
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.
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.)
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.
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.)
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>
Related
By default, Tomcat sends some HTML content back to the client if it encounters something like an HTTP 404. I know that via web.xml an <error-page> can be configured to customize this content.
However, I'd just like for Tomcat to not send anything in terms of response content (I'd still like the status code, of course). Is there any way to easily configure this?
I'm trying to avoid A) explicitly sending empty content on the response stream from my Servlet, and B) configuring custom error pages for a whole bunch of HTTP error statuses in my web.xml.
For some background, I'm developing an HTTP API and am controlling my own response content. So for an HTTP 500, for example, I'm populating some XML content on the response containing error information. For situations like an HTTP 404, the HTTP response status is sufficient for clients, and the content tomcat is sending is unnecessary. If there's a different approach, I'm open to hearing it.
Edit:
After continued investigation, I still can't find much in the way of a solution. If someone can definitively say this is not possible, or provide a resource with evidence that it will not work, I'll accept that as an answer and try and work around it.
If you do not want tomcat to show an error page, then do not use sendError(...). Instead use setStatus(...).
e.g. if you want to give a 405 response, then you do
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
response.getWriter().println("The method " + request.getMethod() +
" is not supported by this service.");
Also remember not to throw any Exceptions from your servlet. Instead catch the Exception and, again, set the statusCode your self.
i.e.
protected void service(HttpServletRequest request,
HttpServletResponse response) throws IOException {
try {
// servlet code here, e.g. super.service(request, response);
} catch (Exception e) {
// log the error with a timestamp, show the timestamp to the user
long now = System.currentTimeMillis();
log("Exception " + now, e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().println("Guru meditation: " + now);
}
}
of course, if you do not want any content, then just don't write anything to the writer, just set the status.
Although this doesn't respond exactly to the "not send anything" statement on the question, and on the wave of Clive Evans' answer, I found out that in tomcat you can make those too much verbose texts go away from error pages without creating a custom ErrorReportValve.
You can accomplish to this customizing ErrorReportValve through the 2 params "showReport" and "showServerInfo" on your "server.xml":
<Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false" />
Link to official documentation.
Worked for me on tomcat 7.0.55, didn't work for me on tomcat 7.0.47 (I think because of something reported on the following link http://www.mail-archive.com/users#tomcat.apache.org/msg113856.html)
The quick, slightly dirty, but easy way of stopping Tomcat from sending any error body is to call setErrorReportValveClass against the tomcat host, with a custom error report valve which overrides report to do nothing. ie:
public class SecureErrorReportValve extends ErrorReportValve {
#Override
protected void report(Request request,Response response,Throwable throwable) {
}
}
and set it with:
((StandardHost) tomcat.getHost()).setErrorReportValveClass(yourErrorValveClassName);
If you want to send your message, and just think Tomcat shouldn't mess with it, you want something along the lines of:
#Override
protected void report(final Request request, final Response response, final Throwable throwable) {
String message = response.getMessage();
if (message != null) {
try {
response.getWriter().print(message);
response.finishResponse();
} catch (IOException e) {
}
}
}
Although it's Servlet spec compliant, for security reasons I don't want tomcat or any other Servlet container to send error details. I struggled with this as well a bit. After searching and trying, the solution can be summed up as:
as others mentioned, don't use sendError(), use setStatus() instead (in Jersey framework you can choose)
frameworks like e.g. Spring Security use sendError() though...
write a Filter that
a. redirects calls to sendError() to setStatus()
b. flushes the response at the end to prevent the container from further modifying the response
A little example servlet filter doing this can be found here.
As Heikki said, setting the status instead of sendError() causes the Tomcat not touch the response entity/body/payload.
If you only want to send the response headers without any entity, like in my case,
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
does the trick. With Content-Length: 0, the print() will have no effect even if used, like:
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
response.getWriter().print("this string will be ignored due to the above line");
the client receives something like:
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 0
Date: Wed, 28 Sep 2011 08:59:49 GMT
If you want to send some error message, use the setContentLength() with message length (other than zero) or you can leave it to the server
Though this question is a bit old, I ran into this problem too. First of all, Tomcat's behavior is absolutely correct. This is per Servlet Spec. One should not alter Tomcat's behavior against the spec. As Heikki Vesalainen and mrCoder mentioned, use setStatus and setStatus only.
To whom it may concern, I have raised a ticket with Tomcat to improve the docs of sendError.
Configure <error-page> Elements in web.xml
Edit $CATALINA_HOME/conf/web.xml, add at the end the following <error-page>, save and restart tomcat
<web-app>
...
...
...
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/400.html</location>
</error-page>
</web-app>
It works great as I expect even though I didn't actually created a valid routes for those specified location values (e.g. /400.html)
before
after
Why not just configure the <error-page> element with an empty HTML page?
I am referring to this project on Github. So this is supposed to be a RESTful API for managing a movie rental service. I mean it technically "works" right now, but one of the things it will do is deliver the error messages directly to the client from the internal methods.
Take this code for example:
/*
GET films
*/
get("/films", (req, res) -> {
try {
String json_output = get_film_list();
return json_output;
} catch (Exception e) {
return "{\"error\":\"There was an error: " + e.toString().replace("\"","") + "\"}";
}
});
And we have the get_film_list() method:
public static String get_film_list() throws SQLException, URISyntaxException{
Connection connection = ConnectionPool.getDBCP2Connection();
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM films");
String output = "{\"films\":[";
int got_result = 0;
while (rs.next()) {
output += "{\"id\":\""+rs.getInt(1)+"\",\"name\":\""+rs.getString(2)+"\",\"type\":\""+rs.getInt(3)+"\"},";
got_result = 1;
}
rs.close();
stmt.close();
output = output.substring(0, output.length()-1) + "]}";
if (got_result == 1){
return output;
}else{
throw new SQLException("No films.");
}
}
So the errors are delivered to the user via the API. I found this pretty convenient for development. I knew that if the server response contains the error property then there was an error, and it supplies the error message. I have heard through code review that this is not the way to do this at all. I also suspect that perhaps you're supposed to give formal HTTP errors or something. But at the same time I figured I would want my api to always return nice JSON formatted strings.
When a user hasn't authenticated, they will see this:
{"error":"Please Authenticate."}
I created an error in the DB connection class, and the user would see this:
{"error":"There was an error: java.sql.SQLException: Cannot load JDBC
driver class 'org.postgresql.Drive'"}
So, my question comes down to, what is the proper way to return error messages to the users with a RESTful API. One of this sort which uses returns JSON data.
Thanks!
RESTful services are based on 2 things, the response code and the actual response itself.
Well, basically it boils down to what error you want to handle. This particular scenario means no data being found and you would find different ways of handling this scenario. Any other error conditions would be handled differently
The 2 approaches to handling this error are
Scenario 1:
Response Code: 200 OK
Response: {}
which means that there was no data for the request specified(more so the parameters supplied with the request)
Scenario 2:
Response Code: 404 Not Found
Response: {"error":"Error Message"}
but this could potentially be confusing to indicate that the service was not found. But this depends on how you've defined your RESTful API.
From what I understand, the above scenario is a mix of both, where it sends out a 200 OK, but at the same time an error message too which is not the way to do it.
Its best to read through the rules of REST and then come up with your API.
Also it might be worth documenting your API through SWAGGER or RAML which makes it meaningful to someone using the service without going through tons of code to understand it.
Since you're using http you should use the http status codes properly, for example the SQL exception would probably result in a response code of 500 Internal Server Error, but you shouldn't expose the actual stack trace or exception at least for two reasons
The api-user has no use of that error message, he can't act upon it or take any reasonable actions to fix it.
You're exposing the applications internals, this could provide someone with malicious intent with valuable information.
When it comes to actually displaying an error. Hopefully something that the user can have some sort of use of. You can pretty much do it in any manner you feel fits your api. The important thing is that the api is consistent.
I'd say that the body of the response you're giving now is okay, except for the fact that the actual message probably doesn't mean anything to the intended user when you just call toString() on an Exception, that information is intended for the developers and should probably be logged.
What you need to do is, translate the exceptions to usable error messages and use http status codes.
When creating a REST API on the top of the HTTP protocol, the HTTP status codes are the proper way to indicate the result of the operation. Along with the status code, you can return a message with more details about the error.
Find below the most common status codes of errors for situations when the client seems to have erred:
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
422 Unprocessable Entity
Don't define your own status codes. Stick to the standards.
When returning a message, your HTTP response can be like:
HTTP/1.1 404 Not Found
Content-Type: application/json
{ "error" : "Resource not found with the requested identifier" }
I have a small REST-ful Java back-end that some clients written in C connect to. There is a certain POST method they call where they send some information, I save that to a database and respond with 200 OK, if all goes well. Basic structure shown below.
#POST
#Path("/mypath")
#Produces("text/html")
public Response processMessage(final String message, #Context final HttpServletRequest request) throws IOException {
.....
return Response.ok().build();
}
My issue is that on this response, I get the following error in the log:
javax.ws.rs.NotAllowedException: No resource method found for , return 405 with Allow header
I understand what this error means in circumstances when let's say you try to execute a GET on an endpoint that is supposed to be a POST, for example. I can't understand though why I would get this after my response goes out, and it clearly shows that the request type is empty.... so odd.
Some additional info - the code on the client side has been buggy with incorrect HTTP code... but what would have to be wrong on the client side to cause this kind of response? (I do not have access to the client side code).
Also, there is no client side code in my app, if you are wondering if there is some other code making a call out of my webapp.
Thanks for any ideas!
The issue is most likely on the client side, so without seeing that code it is difficult to offer more detailed information -- but my expectation would be that this is a result of your client attempting to do something like POST or PUT credentials, or something along that line.
Your only recourse is to enable verbose request logging, log the requests that are generating 405 errors, and report it to the client developers.
Worth noting, however, that any publicly-exposed APIs will generate piles of 405s and 404s because "hackers" will try to execute things like WordPress hacks and so on against any known URL.
I do a ajax call to get a list of items but it returns HTTP error 500.
I traced it in server and map is populated without error. No exception is thrown.
#ResponseBody
#RequestMapping(value = "some/path", method = RequestMethod.POST )
public JsonMessage getlist(#RequestParam(value = "id") Long id){
MyObject myobject = myObjectManager.get(id);
Map map = new HashMap<>();
map.put("shits",lottery.getMultipleRewards());
return JsonMessage.Success(map);
}
any ideas?
(If the map is empty or I set something static like a string on it then it works).
I've seen this happening with particularly demanding implementations when the client didn't include the proper Accept: application/json header or the server didn't include the Content-Type: application/json.
Since the error occurs outside of your code the logging might be generated in an unexpected way/place and you might have suppressed that particular logger.
Another thing that I can think of is that you might be proxy-ing the request through another server. In this case the server that handles the request can generate a correct response but the proxy throws an error for some reason.
Try return a string
if works try simplest object(with xml root)
if it works try to return single object without root.
if it works try map of strings
check if all loggers are set correctly.
Your configuration and used technology might be helpful in diagnose.
I am writing a REST application using Tomcat and Spring WebMVC.
I want to signal errors to my client using HTTP status codes along with some XML payload that contains more information about what went wrong.
To catch all errors regardless of where they occur, I have written a Filter which wraps the response and overrides the sendError() method:
private static final class GenericErrorResponseWrapper
extends HttpServletResponseWrapper
{
#Override
public void sendError(int sc, String msg) throws IOException {
final HttpServletResponse wrappedResponse = (HttpServletResponse) getResponse();
wrappedResponse.setStatus(sc, msg);
wrappedResponse.setContentType("application/xml");
PrintWriter writer = wrappedResponse.getWriter();
try {
SimpleXmlWriter xmlWriter = SimpleXmlWriterWrapper.newInstance(writer);
xmlWriter.writeStartElement("ns2", "genericError")
.writeAttribute("xmlns:ns2", "http://mynamespace")
.writeCharacters(msg)
.writeEndDocument().flush();
writer.flush();
wrappedResponse.flushBuffer();
} finally {
writer.close();
}
}
}
This implementation has two problems:
It generates a deprecation warning in Eclipse, since HttpServletResponse.setStatus(sc, msg) is deprecated.
The HTTP response header generated by Tomcat is not correct, it starts with the first line "HTTP/1.1 500 OK". 500 is correct, but instead of OK the reason phrase should be "Internal Server Error".
How can I implement my filter so that it does the right thing and is free of deprecation warnings? Both alternatives named in the Javadoc are not usable for me:
sendError(sc, msg) is not usable, since it commits the response body and I can't write XML payload any more
setStatus(sc) with just the error code is theoretically usable, but it also creates the hardcoded "OK" string in the first line of the response header.
There is unfortunately no way to avoid the deprecation warning. As you already mention yourself, the two alternatives which are referred to in the API documentation do not cover the same functionality. You may of course annotate your method with #SuppressWarnings("deprecation") to indicate that the usage of the deprecated method is intended.
The other thing, that Tomcat does not use your message string, even if one is provided, is a configuration issue. For some strange reason, Tomcat will by default ignore the provided message string and use a default error message based on the passed return code. You must set the system property org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER to true to force Tomcat to use your provided error message instead. More details on this can be found in the Tomcat documentation.
As an alternative answer - you could first write the XML payload, without calling flush/flushBuffer, and only after that do sendError(int, String), which would flush the buffer.