I use the following Exceptionmapper to map WebApplicationExceptions in my jaxrs rest api to responses.
#Provider
public class ErrorHandler implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException e) {
int status = e.getResponse().getStatus();
JsonObject errorResponse = Json.createObjectBuilder()
.add("status", status)
.add("message", e.getMessage())
.build();
return Response.status(status)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
This works fine and it does exactly what it should do, but when I throw custom errors, for example throw new NotFoundException("custom message"); the stacktrace shows up in my server log. Can anyone explain this? Or does anyone know of a solution?
TL;DR;
For some reason when I throw WebApplicationExceptions from my jax-rs code, my ExceptionMapper handles the error but still throws it so it shows up in the server log.
Any solutions?
I've found the origin of this problem and managed to solve it.
From the JAX-RS spec
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
In my ExceptionMapper I used the WebApplicationException, so every error would be mapped. The problem is that WebApplicationException is not the nearest superclass of (e.g.) NotFoundException. There is a ClientErrorException superclass inbetween. When I changed my mapper to that class the problem was solved.
Since I only want to map client errors to Json responses this solution works fine for me.
Related
Lets say we have a rest service defined as:
#GET
#Produces("application/json")
public Response getAllCategories(#QueryParam(value="startIndex") int startIndex, #QueryParam(value="size") int size)
{
logger.info("[SampleCategoryController][getAllCategories]");
List<YpCategory> categoryList = sampleCategoryService.getAllCategories(startIndex, size);
return Response.ok(categoryList).build();
}
and the service is defined as:
public class SampleCategoriesServiceImpl {
public List<YpCategory> getAllCategories(int startIndex, int size) {
...
//call some method that throws a runtime exception
...
}
}
And an Application Exception handler:
#Provider
#Component
public class ApplicationExceptionHandler implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
String internalError = "There was a problem processing your request.";
return Response.serverError().entity(new ExceptionResponse(500, internalError)).build();
}
}
}
Exception response object: Let the exception bubble up to the ApplicationExceptionHandler and return the ExceptionResponse Object. This way seems cleaner because the service doesn't have to try to handle an exception that it can't really do anything with and the client will still receive a json response.
Response wrapper: The category object would extend some type of generic response wrapper object with information about error codes then I would always have to wrap the method that can throw a runtime exception in a try/catch block and set the error codes and message info in the catch block.
Is one of these ways preferred? Are there cons to using either one of these methods to handle errors?
I think you should use the ExceptionMapper in this case. It is cleaner to let exceptions be handled outside of your implementation.
Also your implementation should be as less possible aware of HTTP. The less your implementation knows about the other parts of your framework the more flexible it will become.
To give an example. Lets say that in the future there is support for a non-HTTP protocol and error messaging will go different then using HTTP status code. You can do the implementation at the level of ExceptionMapper without changing your implementation. Otherwise you have to refactor your application to be aware of the non-HTTP protocol.
Just to be clear, I don't say there is an other implementation available now. It is just a theory.
I have implemented some custom exceptions like: NotFoundException, BadRequestException,
and for each of them I have implemented its own ExceptionMapper like NotFoundExceptionMapper, BadRequestExceptionMapper and also something like GenericExceptionMapper:
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
#Override
#Produces( {MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON} )
public Response toResponse(Throwable ex) {
ErrorResponseWrapper errorResponse = new ErrorResponseWrapper(ex.getMessage(), 500, "");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorResponse).build();
}
}
Firstly the problem here is that GenericExceptionMapper doesn't work as I expected it doesn't catch generic exceptions thrown by JAX-RS as I think each Exception to be caught by such Mapper must be early explicitly thrown from the method like getResource() throws SomeException {
Moreover the problem is that in RestEasy I need to register exception mappers in web.xm. See below:
<!-- Custom exception handling provider -->
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>pl.salonea.jaxrs.exceptions.GenericExceptionMapper</param-value>
</context-param>
Here is another problem as I can not register (or I don't know how to register) each custom ExceptionMapper I have defined. I can only register SINGLE custom ExceptionMapper.
How to register all of them? i.e. NotFoundExceptionMapper, BadRequestExceptionMapper, etc.
So now each exception is mapped only by Generic Exception Mapper and that is the first problem.
Is this limitation of RESTEasy? in Jersey on some tutorial I haven't seen the need to register such ExceptionMappers but I also don't know whether there can be more than one exception Mapper.
Another solution (workaround) that came to my mind is to have single GenericExceptionMapper and in the method toResponse(Throwable ex) makes some checks like:
if(ex instanceof NotFoundException) { // send response type 1
if(ex instanceof BadRequestException) { // send response type 2
Thx for help
I have a RESTful resource, which calls a EJB to make a query. If there is no result from the query, the EJB throws a EntityNotFoundException. In the catch block, it will be thrown a javax.xml.ws.http.HTTPException with code 404.
#Stateless
#Path("naturezas")
public class NaturezasResource {
#GET
#Path("list/{code}")
#Produces(MediaType.APPLICATION_JSON)
public String listByLista(
#PathParam("code") codeListaNaturezasEnum code) {
try {
List<NaturezaORM> naturezas = this.naturezaSB
.listByListaNaturezas(code);
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(naturezas);
} catch (EntityNotFoundException e) { // No data found
logger.error("there is no Natures with the code " + code);
throw new HTTPException(404);
} catch (Exception e) { // Other exceptions
e.printStackTrace();
throw new HTTPException(500);
}
}
}
When I call the Rest Service with a code for which there are no results, the log message inside the EntityNotFoundException catch block is printed. However, my client receives a HTTP code 500 instead a 404. Why am I not receiving a 404 code?
Thanks,
Rafael Afonso
javax.xml.ws.http.HTTPException is for JAX-WS. JAX-RS by default doesnt know how to handle it unless you write an ExceptionMapper for it. So the exception bubbles up to the container level, which just sends a generic internal server error response.
Instead use WebApplicationException or one of its subclasses. Here a list of the exceptions included in the hierarchy, and what they map to (Note: this is only in JAX-RS 2)
Exception Status code Description
-------------------------------------------------------------------------------
BadRequestException 400 Malformed message
NotAuthorizedException 401 Authentication failure
ForbiddenException 403 Not permitted to access
NotFoundException 404 Couldn’t find resource
NotAllowedException 405 HTTP method not supported
NotAcceptableException 406 Client media type requested
not supported
NotSupportedException 415 Client posted media type
not supported
InternalServerErrorException 500 General server error
ServiceUnavailableException 503 Server is temporarily unavailable
or busy
You can find them also in the WebApplicationException link above. They will fall under one of the direct subclasses ClientErrorException, RedirectionException, or ServerErrorException.
With JAX-RS 1.x, this hierarchy doesn't exist, so you would need to do something like #RafaelAlfonso showed in a comment
throw new WebApplicationException(Response.Status.NOT_FOUND);
There are a lot of other possible constructors. Just look at the API link above
I have a spring boot application.
I have a custom error controller, that is mapped to using ErrorPage mappings. The mappings are largely based on HTTP Status codes, and normally just render a HTML view appropriately.
For example, my mapping:
#Configuration
class ErrorConfiguration implements EmbeddedServletContainerCustomizer {
#Override public void customize( ConfigurableEmbeddedServletContainer container ) {
container.addErrorPages( new ErrorPage( HttpStatus.NOT_FOUND, "/error/404.html" ) )
}
And my error controller:
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFound( HttpServletRequest request ) {
"errors/404"
}
This works fine - If I just enter a random non-existent URL then it renders the 404 page.
Now, I want a section of my site, lets say /api/.. that is dedicated to my JSON api to serve the errors as JSON, so if I enter a random non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to have a #ControllerAdvice that specifically caught a class of custom API exceptions I had defined and returned JSON, and in my standard ErrorController checking the URL and throwing an apprpriate API exception if under that API URL space (but that didn't work, as the ExceptionHandler method could not be invoked because it was a different return type from the original controller method).
Is this something that has been solved?
The problem was my own fault. I was trying to work out why my #ExceptionHandler was not able to catch my exception and return JSON - As I suggested at the end of my question, I thought I was having problems because of conflicting return types - this was incorrect.
The error I was getting trying to have my exception handler return JSON was along the lines of:
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation"
I did some more digging/experimenting to try to narrow down the problem (thinking that the issue was because I was in the Spring error handling flow and in an ErrorController that was causing the problem), however the problem was just because of the content negotiation stuff Spring does.
Because my errorPage mapping in the web.xml was mapping to /error/404.html, Spring was using the suffix to resolve the appropriate view - so it then failed when I tried to return json.
I have been able to resolve the issue by changing my web.xml to /error/404 or by turning off the content negotiation suffix option.
Now, I want a section of my site, lets say /api/.. that is dedicated
to my JSON api to serve the errors as JSON, so if I enter a random
non-existent URL under /api/.. then it returns 404 JSON response.
Is there any standard/best way to do this? One idea I tried out was to
have a #ControllerAdvice that specifically caught a class of custom
API exceptions I had defined and returned JSON, and in my standard
ErrorController checking the URL and throwing an apprpriate API
exception if under that API URL space (but that didn't work, as the
ExceptionHandler method could not be invoked because it was a
different return type from the original controller method).
I think you need to rethink what you are trying to do here. According to HTTP response codes here
The 404 or Not Found error message is an HTTP standard response code
indicating that the client was able to communicate with a given
server, but the server could not find what was requested.
So when typing a random URL you may not want to throw 404 all the time. If you are trying to handle a bad request you can do something like this
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ErrorResponse> noRequestHandlerFoundExceptionHandler(NoHandlerFoundException e) {
log.debug("noRequestHandlerFound: stacktrace={}", ExceptionUtils.getStackTrace(e));
String errorCode = "400 - Bad Request";
String errorMsg = "Requested URL doesn't exist";
return new ResponseEntity<>(new ErrorResponse(errorCode, errorMsg), HttpStatus.BAD_REQUEST);
}
}
Construct ResponseEntity that suites your need.
I currently have a RESTful webservice running Jersey. I recently added a filter that does some auth stuff, and it works in the happy-path case. However, when I need to throw an error from within this filter, instead of serializing the exception into a pretty json string it, it throws a 500 with the following error:
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message
body writer for Java class myclass, and Java type class myclass, and MIME media type
application/octet-stream was not found
The thing is, I don't want to write anything to application/octet-stream. My service only uses application/json. This is not a problem in my actual Resource classes, where I can specify the#Produces annotation. Error responses thrown from the body of a resource will serialize properly.
My question, then, is: How do I control what MIME type is used for exceptions thrown while filtering?
You need to build an exceptionmapper to handle the exceptions and turn them in to JSON. Something like:
#Provider
public class UnexpectedExceptionMapper implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(final Exception exception)
{
ResponseBuilder builder = Response.status(Status.BAD_REQUEST)
.entity(jsonError(exception))
.type(MediaType.APPLICATION_JSON);
return builder.build();
}
private String jsonError(final Exception exception)
{
return "{\"error\":\"" + exception.getMessage() + "\"}";
}
}