ResponseBuilder gives 200 OK HTTP response even for server error - java

I am using javax.ws.rs.core.Response api to build and send back response to front end from my spring boot backend. I have a controller as follows.
#GetMapping(value = "/details")
public Response getDetails() throws ServiceException {
try {
//logic to get details
return Response.status(Response.Status.OK).entity(details).build();
} catch (Exception e) {
log.error(e.getMessage());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage).build();
}
}
But I am getting 200 OK as the response status for the HTTP Request in the browser/postman even in the case of exceptions. Because of this, the ajax success block executes instead of the error block when there is an exception. Is there any way to make the error block execute in this case? I don't want to throw an exception as it sends the entire stack trace in the response.

#GetMapping is from spring mvc and spring-mvc is not JAX-RS compliant. So don't mix to use them together. Either use pure spring-mvc or pure JAX-RS . you can try to return a spring-mvc ResponseEntity (equivalent to JAX-RS Response) instead:
#GetMapping(value = "/details")
public ResponseEntity getDetails() throws ServiceException {
try {
//logic to get details
return ResponseEntity.ok(details);
} catch (Exception e) {
log.error(e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage);
}
}

Related

How to parse reponse body from REST API when response code is 4xx in java

I am trying to access REST API using OauthClient
try {
OAuthClient client = new OAuthClient(new URLConnectionClient());
OAuthResourceResponse response = client.resource(request, OAuth.HttpMethod.POST, OAuthResourceResponse.class);
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
The api call returns a response body when I execute the call using Postman, but when I use this code above, it throws exception and I can not see the response body, in order to parse it.
Here is the exception:
org.apache.oltu.oauth2.common.exception.OAuthSystemException: java.io.IOException: Server returned HTTP response code: 409 for URL:
Is it possible to parse the response body for 4xx errors
You can build your response object in catch block and return like
} catch (Exception ex) {
return Response.status(Response.Status.BAD_REQUEST).entity(new PresenterClass(ex.getMessage())).build();
}
Using Presenter class constructor
Public PresenterClass(String errorMessage){
this.message = errorMessage;
}

HttpClientErrorException 400 null using RestTemplate in microServices

I have two microservices. The first one receives a call from the Frontend and then it calls to the second uService to receive some data. The last is returning an error response (Bad Request, this is ok - it is a use-case). However, I am losing the body (message) returned from the second microservice, as the first is throwing a HttpClientErrorException 400 null in the call
This is my code:
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
I am not able to do entityResponse.getStatusCode() as an exception is thrown.
Handled it in the ControllerAdvice, my exception message is "400 null" even I return a custom message from the service.
So, I would like to get the response message sent in the called uservice to manage it.
Thanks in advance.
The answers here that explain how to catch the exception and access the body are correct. However, you may use a different approach. You can use a 3-d party library that sends Http request and handles the response. One of the well-known products would be Apache commons HTTPClient: HttpClient javadoc, HttpClient Maven artifact. There is by far less known but much simpler HTTPClient (part of an open source MgntUtils library written by me): MgntUtils HttpClient javadoc, MgntUtils maven artifact, MgntUtils Github. Using either of those libraries you can send your REST request and receive response independently from Spring as part of your business logic
What I'm doing in my project is the following.
MicroService_2 calls MicroService_1.
MicroService_1
MicroService_1 returns for example a HTTP 404 exception if the entity isn't found.
#RestController
#RequestMapping(value = "/api/v1/")
public class Service1Controller {
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public #ResponseBody MyEntity getMyEntity(#PathVariable String id) throws NotFoundException {
MyEntity result = ...
if(result == null) {
throw new NotFoundException("MyEntity [id: "+id+"] not found");
}
return result;
}
#ControllerAdvice
public class RestEndpointExceptionHandler extends RestExceptionHandler {
#ExceptionHandler(NotFoundException.class)
public ResponseEntity<String> handleNotFoundException(HttpServletRequest req, NotFoundException ex) throws NotFoundException {
return new ResponseEntity<String>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
}
}
MicroService_2
The MicroService_2 calls MicroService_1 and catches the exception by HTTP code and regenerate the NotFoundException.
#Override
public MyEntity getMyEntity(Principal principal) {
try {
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
return entityResponse.getBody();
} catch(HttpClientErrorException e) {
HttpStatus status = e.getStatusCode();
if (status == HttpStatus.NOT_FOUND) {
throw new NotFoundException(e.getResponseBodyAsString()); // should be "MyEntity [id: {id}] not found"
} else {
throw new UnexpectedServerException(e.getResponseBodyAsString());
}
}
}
The Spring RestTemplate throws an error in case of 500 or 400 status codes. So if your second service responds with an error an exception will be thrown by the RestTemplate call in your first service.
HttpClientErrorException: in case of HTTP status 4xx
HttpServerErrorException: in case of HTTP status 5xx
UnknownHttpStatusCodeException: in case of an unknown HTTP status
To get the response message you could either catch the exception. E.g:
try {
ResponseEntity<MyEntity> entityResponse = restTemplate.getForEntity(url, MyEntity.class, id);
} catch(HttpStatusCodeException e) {
// e.getResponseBodyAsString();
}
or define a ResponseErrorHandler. The ResponseErrorHandler can be set during the instantiation of the RestTemplate. In the handleError method you will also be able to access the response message.
#Override
public void handleError(ClientHttpResponse httpResponse)
throws IOException {
}

Rest Api exception handling

I am having different projects for Service and Web. I would like to know how to handle when specific exception comes from Services. For example I am handling DuplicateDataException as follows at Service side:
public void serviceFunction()
{
try
{
//code
}catch(DuplicateDataException e)
{
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(e.getMessage()).build();
}}
At UI side: controller class is calling the service function through Rest API
#RequestMapping(value = "/addNew", method = RequestMethod.POST)
public ModelAndView addNew(Object obj) {
try {
restTemplate.exchange(url, HttpMethod.POST, httpEntity,
Object.class);
LOGGER.info("Object Created Successfully");
} catch (Exception e) {
return ModelAndView("PageName", "param","value");
}
}
At UI side I am getting Internal Server Error, Instead I would like to get the entity error message value which was set at service side.
As a kind of best practice try to catch your exceptions in your service code and throw an RuntimeException("An error occured") or a self defined Exception which extends Java's RuntimeException. Then you can define a global ExceptionHandler for all of your controllers and return your own error page like:
#ControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(Exeption.class)
public ModelAndView handleFileNotFoundException(Exception exception){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("yourView");
modelAndView.addObject("exception", exception);
return modelAndView;
}
}

Using Spring MVC, accepting POST requests with bad JSON leads to a default 400 error code server page being returned

I am working on a REST api. Receiving a POST message with bad JSON (e.g. { sdfasdfasdf } ) causes Spring to return the default server page for a 400 Bad Request Error. I do not want to return a page, I want to return a custom JSON Error object.
I can do this when there is an exception thrown by using an #ExceptionHandler. So if it is a blank request or a blank JSON object (e.g. { } ), it will throw a NullPointerException and I can catch it with my ExceptionHandler and do whatever I please.
The problem then, is that Spring doesn't actually throw an exception when it is just invalid syntax... at least not that I can see. It simply returns the default error page from the server, whether it is Tomcat, Glassfish, etc.
So my question is how can I "intercept" Spring and cause it to use my exception handler or otherwise prevent the error page from displaying and instead return a JSON error object?
Here is my code:
#RequestMapping(value = "/trackingNumbers", method = RequestMethod.POST, consumes = "application/json")
#ResponseBody
public ResponseEntity<String> setTrackingNumber(#RequestBody TrackingNumber trackingNumber) {
HttpStatus status = null;
ResponseStatus responseStatus = null;
String result = null;
ObjectMapper mapper = new ObjectMapper();
trackingNumbersService.setTrackingNumber(trackingNumber);
status = HttpStatus.CREATED;
result = trackingNumber.getCompany();
ResponseEntity<String> response = new ResponseEntity<String>(result, status);
return response;
}
#ExceptionHandler({NullPointerException.class, EOFException.class})
#ResponseBody
public ResponseEntity<String> resolveException()
{
HttpStatus status = null;
ResponseStatus responseStatus = null;
String result = null;
ObjectMapper mapper = new ObjectMapper();
responseStatus = new ResponseStatus("400", "That is not a valid form for a TrackingNumber object " +
"({\"company\":\"EXAMPLE\",\"pro_bill_id\":\"EXAMPLE123\",\"tracking_num\":\"EXAMPLE123\"})");
status = HttpStatus.BAD_REQUEST;
try {
result = mapper.writeValueAsString(responseStatus);
} catch (IOException e1) {
e1.printStackTrace();
}
ResponseEntity<String> response = new ResponseEntity<String>(result, status);
return response;
}
This was raised as an issue with Spring SPR-7439 - JSON (jackson) #RequestBody marshalling throws awkward exception - which was fixed in Spring 3.1M2 by having Spring throw a org.springframework.http.converter.HttpMessageNotReadableException in the case of a missing or invalid message body.
In your code you cannot create a ResponseStatus since it is abstract but I tested catching this exception with a simpler method locally with Spring 3.2.0.RELEASE running on Jetty 9.0.3.v20130506.
#ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String resolveException() {
return "error";
}
and I received a 400 status "error" String response.
The defect was discussed on this Spring forum post.
Note: I started testing with Jetty 9.0.0.M4 but that had some other internal issues stopping the #ExceptionHandler completing, so depending on your container (Jetty, Tomcat, other) version you might need to get a newer version that plays nicely with whatever version of Spring you are using.

Spring MVC: using #ResponseStatus(reason = '') on a #ResponseBody exception handler in tomcat

Does anybody know why I cannot use #ResponseStatus(reason = "My message") on an exception handler in spring MVC while still returning a #ResponseBody. What seems to happen is that if I use the reason attribute
// this exception handle works, the result is a 404 and the http body is the json serialised
// {"message", "the message"}
#ExceptionHandler
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public Map<String, String> notFoundHandler(NotFoundException e){
return Collections.singletonMap("message", e.getMessage());
}
// this doesn't... the response is a 404 and the status line reads 'Really really not found'
// but the body is actually the standard Tomcat 404 page
#ExceptionHandler
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Really really not found")
public Map<String, String> reallyNotFoundHandler(ReallyNotFoundException e){
return Collections.singletonMap("message", e.getMessage());
}
The code for this example is over on github.
It seems that this is a direct result of the following code from AnnotationMethodHandlerExceptionResolver
private ModelAndView getModelAndView(Method handlerMethod, Object returnValue, ServletWebRequest webRequest)
throws Exception {
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
if (responseStatusAnn != null) {
HttpStatus responseStatus = responseStatusAnn.value();
String reason = responseStatusAnn.reason();
if (!StringUtils.hasText(reason)) {
// this doesn't commit the response
webRequest.getResponse().setStatus(responseStatus.value());
}
else {
// this commits the response such that any more calls to write to the
// response are ignored
webRequest.getResponse().sendError(responseStatus.value(), reason);
}
}
/// snip
}
This has been reported to Springsource in SPR-8251:
For the record, since Spring 3.2, this got even worse because the AnnotationMethodHandlerExceptionResolver has been replaced by the ResponseStatusExceptionResolver and it does:
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.value().value();
String reason = responseStatus.reason();
if (this.messageSource != null) {
reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());
}
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);
}
else {
response.sendError(statusCode, reason);
}
return new ModelAndView();
}
This is worth a bug report. Moreover, the #ResponseStatus is documented with setStatus and is ill-designed. It should have been called #ResponseError.
I have created two issues for this finally: SPR-11192 and SPR-11193.
Almost a year has passed and my two issues are still open. I do not consider Spring WebMVC as a first-class REST framework which it isn't imho, WebMVC is for humas and not machines :-(

Categories

Resources