Difference between HttpClientErrorException.getStatusText and HttpClientErrorException.getResponseBodyAsString - java

I use httpResp.sendError(400, "You are missing customer id") to send the response back
When I try to retrieve the message on the client side (using Rest template to call the endpoint).
However, printing the HttpClientErrorException always produces the following result for me:
HttpClientErrorException: 400 null
I see that I have HttpClientErrorException.getResponseBody has all the information about time stamp, message etc. But HttpClientErrorException.getStatusText is always empty.
My question is : How do you design your ResponseEntity on the server-side such that the HTTP client finds the server-side exception message in response.getStatusText() instead of null?
here is the code I have
try{
ResponseEntity<String> responseEntity = restTemplate.exchange(uri, HttpMethod.POST, requestEntity, String.class );
System.out.println(responseEntity.getBody());
}
catch (HttpClientErrorException | HttpServerErrorException e) {
if (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED) || e.getStatusCode().equals(HttpStatus.FORBIDDEN)) { System.out.println("Response Body returned:");
System.out.println(e.getResponseBodyAsString());
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
System.out.println("Status text is:");
System.out.println(e.getStatusText());
} else if (e.getStatusCode().equals(HttpStatus.BAD_REQUEST)) {
System.out.println("Response Body returned:");
System.out.println(e.getResponseBodyAsString());
System.out.println("-------------------------------");
System.out.println("Status text is:");
System.out.println(e.getStatusText());
} else {
throw e;
}
}
Sprint Boot Version: 2.1.0.RELEASE

I traced the code for how RestTemplate actually makes the calls. Basically what happens is the result of HttpClientErrorException.getStatusText() is populated by the HTTP status code's text and not your custom error message. For example, instead of just returning back error code 400, a server might return back error 400 Bad Request. Instead of status code 200, a server might return back 200 OK. If the server responds back with that optional text, that's what you'll see when you call getStatusText(). Note that that text like OK and Bad Request can't be customized by you on the server side.
This is happening because, internally, Spring is making use of SimpleClientHttpResponse.getStatusText() which is internally relying on HttpUrlConnection.getResponseMessage(). As you can see from getResponseMessage's Javadoc, the possible values returned aren't meant to be custom error messages. Note that in your case, getStatusText() is returning null because your server is just sending back a status line like 400 and not 400 Bad Request. You can also see this from the Javadoc. You can probably configure your server to send back status code text messages, but doing so still won't help you use the getStatusText() method to get your custom error message.
Consequently, the HttpClientErrorException.getStatusText() just isn't what you need. Instead, you need to continue calling getResponseBodyAsString(). However, if you know the format of the response body that is sent back from the server (since this will likely be wrapped in HTML and other stuff) you can use a regex to filter out the non-useful parts of the response message.

Related

How does Resttemplate determine the status code of response before receiving it?

I'm new to Java and found a confusing behaviour related with RestTemplate.
It happened with an API returning large body (~5MB) over a quite slow network condition. The code is like below
ResponseEntity<MyEntity[]> result = restTemplate.exchange(url, HttpMethod.GET, entity, MyEntity[].class);
And also a ClientHttpRequestInterceptor is set to log before and after the request.
The confusing thing is that the after request log is logged only a while after remote server giving the response, and the HTTP Status code can be print in the log.
But the above statement took much more time to finally receive the data. Look inside the thread stack, it was reading data from socket.
I also look inside the resttemplate class and found:
response = request.execute();
handleResponse(url, method, response);
if (responseExtractor != null) {
return responseExtractor.extractData(response);
}
It seems to extractData after the execute().
My doubt is:
How does the client side know the status code even before get all the data? It just extracts necessary fields from the top packets?
Since the server has already sent out the response, where the response data is stored during the process?
It stores the data that it receives from the underlying HTTP in memory.
Client side can know what's the status code because with HTTP you get the headers and status code first before the response body. But this doesn't matter with RestTemplate as it promises to give you an object of ResponseEntity in the end, which contains everything from the http response be it status codex headers or body.
RestTemplate is an abstraction over an HttpClient, most client's give you the option to implement callbacks for separate events like onHeadersReceived(), onStatusReceived() etc. But if you are using RestTemplate this means you don't require such fine grained control.

How to get the response body (JSON) in Java from an HTTP delete method when called using the REST template

This is my first question here, I am a beginner in using the REST template and Spring and I apologize if I am asking simple questions.
I am trying to call a delete method from another component using the REST template.
The response I receive in POSTMAN is the following JSON:
{
"code": 100,
"message": "my message"
}
I should not be able to delete the object, so my request fails with org.springframework.web.client.HttpClientErrorException.
In the logs all I see is the:
[Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 400 Bad Request] with root cause...
I have searched a bit and I have seen that when try to call a rest service with restTemplate, the body of response is lost if it returns a 400.
This is the code I have used to catch the HttpClientErrorException:
try{
restTemplate.delete(url);
}
catch (HttpClientErrorException e) {
LOG.error("FM HttpClientErrorException caught");
LOG.error("FM response body : {}", e.getResponseBodyAsString());
LOG.error("FM response headers message: {}", e.getResponseHeaders().toString());
LOG.error("FM response message : {}", e.getMessage());
LOG.error("FM response status : {}", e.getStatusCode());
}
I have seen in other posts
DELETE in Spring RestTemplate with HttpEntity<List>
that one solution is to catch the exception and try to get the body of the response.
However, in my case this is always empty.
I have tried getting the response as well using the exchange from restTemplate and then catching the Exception as above, but my body is still empty:
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, null, String.class);
LOG.info("FM response body : {}", response.getBody());
LOG.info("FM response status : {}", response.getStatusCode());
Unit test result:
Unit tes2018-06-12T15:23:02,960Z [main] ERROR c.v.b.s.impl.ServiceImpl - HttpClientErrorException caught
2018-06-12T15:23:02,960Z [main] ERROR c.v.b.s.impl.ServiceImpl - response body : []
2018-06-12T15:23:02,960Z [main] ERROR c.v.b.s.impl.ServiceImpl - response headers message: {}
2018-06-12T15:23:02,960Z [main] ERROR c.v.b.s.impl.ServiceImpl - response message : 400 Bad Request
2018-06-12T15:23:02,960Z [main] ERROR c.v.b.s.impl.ServiceImpl - response status : 400t results:
The questions are:
Can we retrieve the JSON from the response using a REST delete call?
Can we do the same using the exchange? If yes, how can we retrieve the JSON sent? I tried both and the body is always empty.
I have seen also this post:
What is the HTTP status return code for a successful DELETE statement in REST?
The 3rd question would be then:
Is it a good practice to return a JSON and say why the delete cannot succeed?
Thank you very much, any help or suggestion regarding the code or the solution is appreciated.
1,2) Yes, it is possible. Based on my own experience, I much prefer to use the REST Client that comes with the JAX-RS specification, than the one that comes with Spring.
https://docs.oracle.com/javaee/7/api/javax/ws/rs/client/package-summary.html
You can make any request you like and then just get the body, independent of the result code. Nothing gets lost.
I know this doesn't fully answer your first question, but might be helpful.
3) The best practice is to return different error codes signifying the different reasons. Was it access denied? Was the resource not available? Was there an unexpected error?
You can then document all this fairly easy using Swagger/OpenApi/etc.
These should all be enough to handle the potential exceptions. If this is still not enough, you might want to create support methods that validate that a resource can be deleted (ex: GET /api/users/{id}/isDeletable).

How to keep ResponseBody on 204 No Content response?

I want to return a 204 no content http status code. Though I want to add a custom error messages that gives details why where was no content.
Problem: I'm using spring-mvc, and when returning HttpStatus.NO_CONTENT type, the response body is always removed and empty for the client!
#RestControllerAdvice
public class ExeptionHandler {
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.NO_CONTENT)
public Object handler(HttpServletRequest req, Exception e) {
return new ResponseEntity<String>(e.getMessage(), HttpStatus.NO_CONTENT);
}
}
If I change the type eg to HttpStatus.NOT_FOUND then the error message is shown as response body.
How can I achieve the same with 204?
I want to return a 204 no content HTTP status code. Though I want to add a custom error messages that gives details why where was no content.
You can't.
Problem: I'm using Spring MVC, and when returning HttpStatus.NO_CONTENT type, the response body is always removed and empty for the client!
That's the expected behaviour.
If I change the type eg to HttpStatus.NOT_FOUND then the error message is shown as response body. How can I achieve the same with 204?
You won't achieve it with 204. You are misunderstanding (and probably misusing) such status code. The purpose of 204 is to indicate that the server has successfully fulfilled the request and there's not additional content to send in the response payload.
For reference, see how this status code is defined in the RFC 7231:
6.3.5. 204 No Content
The 204 (No Content) status code indicates that the server has
successfully fulfilled the request and that there is no additional
content to send in the response payload body. [...]
A 204 response is terminated by the first empty line after the header
fields because it cannot contain a message body. [...]
Depending on the situation, you'd better use 404 or 200 with an empty array in the response payload. This answer may give you some insights.
204 is for successful requests. It should have no error message included.
If an error happened, a different HTTP Code should be used.
see https://httpstatuses.com/204
A 204 response is terminated by the first empty line after the header fields because it cannot contain a message body.

How to set the Response Text in Spring REST exception handlers?

I'm working on a Spring REST controller, specifically on an exception handler. The exception handler works as intended and my JUnit-Test (using the Spring HTTP client) shows that the correct HTTP Status code (400) is received at the client. The HTTP client automatically translates this into a HttpClientErrorException.
However, printing the HttpClientErrorException always produces the following result for me:
HttpClientErrorException: 400 null
... and the null part is what worries me. Shouldn't this be the place where the message of the server-side exception is supposed to be?
I checked the source code of the HTTP client to see where the client-side exception is thrown. It looks like this:
throw new HttpClientErrorException(statusCode, response.getStatusText(), response.getHeaders(), getResponseBody(response), getCharset(response));
Debugging this call revealed that response.getStatusText() is null in my case.
My question is: How do you design your ResponseEntity on the server-side such that the HTTP client finds the server-side exception message in response.getStatusText() instead of null?
Mine currently looks like this:
#ExceptionHandler({ MyCustomException.class })
public ResponseEntity<String> handleException(final HttpServletRequest req, final MyCustomException e) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-type", "text/plain");
String body = e.toString();
return new ResponseEntity<>(body, headers, HttpStatus.BAD_REQUEST);
}
... and I get null in the client side status text.
I must admit that I got fooled on this one. The null value printed by the Spring HttpClientErrorException is the statusText. This text is static. For example, for Status Code 404, the defined status text is "not found". There is no way to change it.
In order to receive the actual exception code, then the method suggested by Utku is exactly right. The small gotcha is that the error message needs to be extracted from HttpClientErrorException#getResponseBodyAsString(), not from HttpClientErrorException#getStatusText() like I tried.

Servlet and error conditions

I'm writing my first JAVA servlet and I have a question.
May be it's important to say that my servlet will be called from Google Web Toolkit (AJAX)
First thing that I do is to create PrintWriter and start to write in it my JSON output
PrintWriter out = response.getWriter();
...
out.println("[");
out.println(" {");
out.println(" \"validation\" : {");
...
but what will happened if meanwhile I get an error condition?
What is the right way to return an error to client? (AJAX client)
Do I have to buffer my output (HOW?) and return error as JSON (instead of output)
or
I have to throw ServletException?
Just build the string in memory using for example StringBuilder. Do not write any character to the response yet until you're finished building the string. That's "buffering".
StringBuilder builder= new StringBuilder();
builder.append("[");
builder.append(" {");
builder.append(" \"validation\" : {");
// ...
// When finished:
response.getWriter().write(builder.toString());
When something fails in meanwhile, either throw ServletException (which will end up in a server default error page with status code 500), or use HttpServletResponse#sendError() to send a more specific error status. But generally, status code 500 is enough sign for XMLHttpRequest client that something failed at the server side.
try {
// Build and write JSON.
} catch (Exception e) {
throw new ServletException(e);
}
As #McDowell says, the correct way to deal with an error during request handling in a servlet is to set an HTTP status code in the response object.
But there is a catch.
The HTTP status code is actually passed in the first line of the HTTP response. And that gets written when the response is "committed", which typically happens when you call response.getOutputStream() or response.getWriter(). After that, you cannot change the status code.
The way to deal with it is to do one of the following:
Code your application so that errors don't happen during the generation of the response body.
Generate the response body to a buffer of some kind, and only open the response output stream / reader when you've built it completely. If there are errors during the body generation, you can set the HTTP status code and (if appropriate) send an alternative body containing an error message.
See the HTTP status codes. You can use HttpServletResponse.setStatus to set the response state (note also the constants defined by that class).

Categories

Resources