I'm using Jersey to provide a java REST service to the outside world. I offer some functions that take JSON and I use the Jackson framework in combination with jersey to convert them into POJOs.
I have the problem that if wrong jackson format is sent to the server, the answer (content of the http response) is a jackson specific exception description. If I have a POJO with an attribute "surname" and send "sursname" in the json string to the server, I get:
Unrecognized field "sursname" (Class XY), not marked as ignorable at [Source: sun.net.httpserver.FixedLengthInputStream#903025; line: 1, column: 49] (through reference chain: XY["sursname"])
Thats quite ok, but I would like to have my own response content, for example my own error code. I already wrote a custom ExceptionMapper that should map all Throwables.
#Provider
public class WebserviceExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Exception e) {
e.printStackTrace();
return Response.status(400).entity("{\"errorcode\":\"CRITICAL_ERROR\"}").type(MediaType.APPLICATION_JSON).build();
}
}
It seems that the jackson exception is thrown before my webservice method is called, so i have no chance to map it?
Does anyone have an idea? Big thanks und sorry for my english ;)
The reason that your custom ExceptionMapper doesn't catch the exceptions is because the Jackson library provides its own exception mappers, which catches the exception before it would be catched by your genereal exception mapper.
Some suggest that you should implement your own exception mappers for JsonParseExceptionMapper and JacksonMappingExceptionMapper, but that, however, will give inconsistent results. See this issue on their GitHub.
To solve this problem, you must make sure that none of the built-in exception mappers are registered. If you're using the jackson-jaxrs-providers library for JSON: jackson-jaxrs-json-provider, make sure that you are only registering either JacksonJaxbJsonProvider.class or JacksonJsonProvider.class in your Application class:
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(JacksonJaxbJsonProvider.class)
}
}
Be aware of the JacksonFeature.class, as it registers the built in ExceptionMappers. Also stay away from the jersey-media-json-jackson library, which automatically will add some built in exception mappers, without you having to do anything at all.
I ran into a similar issue a while back while using Jackson and Apache CXF. The exception mappers weren't called if the exception didn't happen in my JAX-RS method and instead happened in the JacksonJsonProvider. The solution in my case was to extend JacksonJsonProvider, catch the specific Jackson json exceptions, and rethrow them as WebApplicationException.
Here's my overriden readFrom method for my extended JacksonJsonProvider:
#Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException {
try {
return super.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream);
} catch (JsonParseException jpe) {
throw new WebApplicationException(jpe, Response.status(Status.BAD_REQUEST)
.entity(new ErrorEntity("Malformed json passed to server: \n" + jpe.getMessage())).build());
} catch (JsonMappingException jme) {
throw new WebApplicationException(jme, Response
.status(Status.BAD_REQUEST)
.entity(new ErrorEntity("Malformed json passed to server, incorrect data type used: \n"
+ jme.getMessage())).build());
}
}
Of course Jackson exception is thrown before: the Jackson provider is called in order to create an object you are expecting in your method. However, with ExceptionMapper you should be able to map not only your exceptions, but also the providers exception.
Are you sure your provider is registered? Does it being called if your throw exception from your method and not from provider?
If the answer to the previous question is "yes", try to implement ExceptionMapper for a concrete exception instead of Throwable.
I had the same problem and solve overriding the ExceptionMapper. Perfect! One extra thing that I needed to do and were not understanding 100% was how to override the JacksonProvider for my application (I don't know if it was related to Jersey's version that I was using - 2.19). Here's my web.xml part that overrides it:
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider
</param-value>
Thanks for your help, an ExceptionMapper for JsonParseException didn't help. I removed the jackson jar files from my projekt and included the jackson sources. Then I modified ord.codehaus.jackson.jaxrs.JsonParseExceptionMapper and JacksonMappingExceptionMapper to return my custom response content. I'm not happy with it, but it works now!
Thanks for your help!
Related
A typical way to deal with exceptions in REST service is to define custom exception types (typically from RuntimeException) and then implement a mapper class to produce HTTP codes, for example:
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyExceptionex) {
return Response.status(400).entity("bad request")
.type(MediaType.APPLICATION_JSON).build();
}
}
Now, I have 2 questions:
How would I implement a mapper for the "default case", meaning for every exception not mapped here or in another mapper? When for example implementing one for Throwable in order to produce an HTTP 500, wouldn't it catch my own exceptions again? Or can an order be defined in which mappers work?
When calling managed components like EJBs from REST service, wouldn't an exception thrown there result in an EJBException or some Transaction...Exception wrapping my own one?
mapper for the "default case"...
Just as Paul Samsotha writes in the comment, the JAX-RS server runtime is supposed to pick the most specific exception mapper. Or to quote the JAX-RS specs (for JEE7/version 2.0):
3.3.4 Exceptions
[...]
If an exception mapping provider (see Section 4.4) is available for the exception or one of its superclasses, an implementation MUST use the provider whose generic type is the nearest superclass of the exception to create a Response instance that is then processed according to Section 3.3.3.[...]
So I guess you can use an exception mapper for Throwable - its signature verifies it anyway:
public interface ExceptionMapper<E extends Throwable> {...}
When calling managed components like EJBs from REST service...
The EJB container will wrap the exception, if it needs to be wrapped. Not all exceptions thrown by EJBs are required to be wrapped. The EJB spec (v3.1) makes the distinction between application exceptions (annotated with javax.ejb.ApplicationException) and "all other exceptions" (see section 14.3).
So make the exception #ApplicationException and provide a mapper for it. But if you still want to respond based on the wrapped exception:
Respond based on the wrapped exception
You cannot directly select a mapper based on the wrapped exception. But you can create an exception mapper for the wrapper exception that unwraps it and selects an appropriate mapper for the wrapped exception based on the Providers context (see JAX-RS 2.0 section 9.2.6 and the javax.ws.rs.ext.Providers Javadocs). An example, untested code for a hypothetical MyWrapperException would be:
#Provider
public class MyWrapperExceptionMapper implements ExceptionMapper<MyWrapperException> {
#Context
private Providers providers;
public Response toResponse(MyWrapperException e) {
Throwable t = e.getCause();
ExceptionMapper mapper = providers.getExceptionMapper(t.getClass());
if( mapper != null ) {
return mapper.toResponse(t);
}
else {
// custom handling...
}
}
}
I need some help with understanding how Websphere Liberty (18.0.0.1) handles exceptions thrown within a JAX-RS endpoint invocation. I'm using Liberty feature jaxrs-2.0, so the implementation should be provided by WLP.
Now, my application has a POST HTTP endpoint accepting JSON payload and I'd like to provide a custom error messages for all the possible wrong client inputs.
Here's one case that works in a way I expected it:
Client sends application/xml instead of application/json
There's a ClientErrorException thrown by the container
I can use my own exception mapper (implementing ExceptionMapper<WebApplicationException> to handle this exception (actually to handle all the web application exception, which I'm fine with)
This way I can format the error message, mark error with ID, whatever is needed. That's good
And here's the case not working for me:
Client sends application/json, but with empty body
The core exception in this case is java.io.EOFException: No content to map to Object due to end of input - yeah, that looks accurate
Now what I can't figure out - instead of wrapping this EOFException into some kind of WebApplicationException (which I could handle easily), WLP is wrapping the exception issue into JaxRsRuntimeException
A couple of points here:
I don't want to create a mapper implementing ExceptionMapper<JaxRsRuntimeException> because that exception is not a part of JAX-RS 2.0 spec and I'd have to provide the import to JaxRsRuntimeException and wire the application with some Liberty-specific library.
A possible solution is to have my mapper implement a generic ExceptionMapper<RuntimeException> and string check if it finds exception of classname 'JaxRsRuntimeException' and then handle it. But that just doesn't seem right to me.
So, is that a WLP design not to give me a WebApplicationException in this case? What would be the elegant solution to handle this scenario?
Thanks
EDIT: Added some parts of source code.
REST endpoint and resource method:
#Path("/books")
public class BookEndpoint {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response createBook(Book book, #Context UriInfo uriInfo) {
bookDao.create(book);
UriBuilder builder = uriInfo.getAbsolutePathBuilder();
builder.path(Integer.toString(book.getId()));
return Response.created(builder.build()).entity(book).build();
}
}
Entity with JAXB annotations:
#XmlRootElement
public class Book {
private int id;
private String title;
// getters, setters
}
Exception stack trace:
com.ibm.ws.jaxrs20.JaxRsRuntimeException: java.io.EOFException: No content to map to Object duto end of input
at org.apache.cxf.jaxrs.utils.JAXRSUtils.toJaxRsRuntimeException(JAXRSUtils.java:1928)
at [internal classes]
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:201)
at [internal classes]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.EOFException: No content to map to Object duto end of input
at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2775)
at [internal classes]
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1413)
at [internal classes]
... 48 more
This is the expected behavior based on Section 3.3.4 (and 4.5.1) of the JAX-RS 2.0 Spec. These sections describe how exceptions from JAX-RS resources and providers are handled - in short:
If the exception is a WebApplicationException, then it will automatically mapped to a Response.
If there is an ExceptionMapper registered that can handle the thrown exception, then that will be used to generate the response.
Unchecked exceptions are propagated to the container (i.e. Liberty's JAX-RS implementation code).
Unmapped exceptions must be handled via a container-specific exception and then appropriately propagated to the underlying container - in this case a ServletException must be passed to the web container.
The JaxRsRuntimeException is used to satisfy step 4.
In this scenario the built-in JSON provider (based on Jackson 1.X) is throwing the EOFException. Since there are no exception mappers for the EOFException (or any of it's superclasses), it is ultimately mapped to a ServletException by way of the JaxRsRuntimeException.
In order for an application to handle this scenario, there are a few different options:
You can register an ExceptionMapper that is specific to this exception type (EOFException or any of it's superclasses - i.e. IOException). You should not need to register a mapper for JaxRsRuntimeException as that exception is only used internally in Liberty - and should not be mapped. If you are seeing the JaxRsRuntimeException passed to an ExceptionMapper, then you should open a support case with IBM, as this is likely a bug.
With an ExceptionMapper<EOFException> you can return a specific response whenever an EOFException is thrown from a provider or resource.
You can register your own MessageBodyReader that will convert JSON to objects (using Jackson or any other JSON serialization code) but that will handle empty message bodies in the way you want - for example, converting it to null or using some kind of default object instance. Because user-registered providers take priority over built-in providers, this MBR would be used instead of Liberty's Jackson-based MBR.
This approach definitely gives you more control over how the data is deserialized as well as the exception handling.
Register a ContainerRequestFilter provider that will abort when the message body is empty. Here is an example:
#Provider
public class EmptyBodyCheckFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
if (crc.getEntityStream().available() < 1) {
crc.abortWith(Response.status(400).entity("Invalid request - empty message body").build());
}
}
}
I've successfully tested options 1 and 3 using the WebSphere Liberty May 2018 Beta. I haven't personally tested option 2 for this scenario, but based on using custom MBRs in the past, this should work.
One thing to keep in mind is that when Liberty GAs the jaxrs-2.1 feature, it will use JSONB as the built-in provider for serializing/deserializing JSON instead of Jackson. I tested your scenario using JAX-RS 2.1 (also in the May Beta) and instead of an EOFException, the JSONB code throws a NoSuchElementException. If you think you might move to JAX-RS 2.1, then I would suggest option 2 or 3. Option 1 would require that you create a new ExceptionMapper for JAX-RS 2.1.
Hope this helps,
Andy
Not a direct answert on "why WLP wrap the exception ..etc" but maybe add an exception interceptor as you did but on"ExceptionMapper<Exception>"and recusrsively iterate on the "causes" to check if java.io.EOFExceptionis one of those...
I have a setup where we're using JAX-RS / RestEasy along with Spring Security. For the most part, it works just fine, but I'm running into an unexpected hiccup with handling exceptions thrown by #PreAuthorize.
We're doing some permission evaluation using SpEL, like so:
#POST
#PreAuthorize("#permHandler.canUserUpdate(#request, authentication)")
That case will either evaluate to true, or throw an exception, UnauthorizedException, with some information.
The problem in this case is with handling the Exception properly and returning the correct error code to the client.
I have a typical JAX-RS expression mapper set up like so:
#Provider
public static class UnauthorizedExceptionMapper implements ExceptionMapper<UnauthorizedException> {
#Override
public Response toResponse(UnauthorizedException exception) {
...
return Response.status(403).entity(error).build();
}
}
This handler never gets hit, because #PreAuthorize doesn't bubble up my UnauthorizedException. Instead, it isn't quite sure what to do and throws an IllegalArgumentException instead. Mapping the IllegalArgumentException to get my 403 response isn't really going to work for obvious reasons.
Is there an accepted way to get this to work with this setup? Most of the spring security exception handling documentation makes specific references to utilities for spring web or mvc, which is not our case here.
Any help or a nod in the right direction would be appreciated.
The solution was to make sure our Exception extended AccessDeniedException - in that case Spring Security was smart enough to bubble it up rather than throw an IllegalArgumentException
Example:
public class UnauthorizedException extends AccessDeniedException
I have a web application in which I throw some custom exceptions(application exceptions annotated with #ApplicationException) and there is an exception mapper(provider annotated with #Provider) for each. Recently I forgot to annotate an exception with #ApplicationException and still the mapper is able to identify the exception and format the response properly.
Then I checked the documentation and I understood that the annotation will be inherited by its child class by default. So I removed the annotation from the super class and checked. The mapper still identified the exception and formatted the response.
Then I went even forward and tried throwing java.lang.IllegalArgumentException and wrote a mapper class for it. It also worked properly. Is javax.ws.rs.ext.ExceptionMapper independent of the exception being thrown. Will it not check if whether thrown exception is really annotated with #ApplicationException?
#Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<java.lang.IllegalArgumentException> {
#Override
public Response toResponse(java.lang.IllegalArgumentException exception) {
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Somewhere in my service class:
throw new java.lang.IllegalArgumentException("Problem with the payload. Please check the payload you are sending");
The answer is no, it will not check if whether thrown exception is really annotated with #ApplicationException.
The exception mapper is independent of the #ApplicationException.
All the exception mapper knows is, if there's no exception caught until the almost last layer, it will be processed here, if it find a matching provider.
You can also actually create a provider for RuntimeException and all exception happened in the REST request will land here (but this is not good practice, we should always throw custom exception and catch them with the provider and convert them to good error response).
When you annotate the exception with #ApplicationException you can control things like whether the transaction should be rollback, and whether it will be wrapped by EJBException etc etc.
I'm looking for way to present custom error pages if no matching JAX-RS resource was found. My idea was to use an ExceptionMapper, but I don't know the exception class to use:
#Provider
public class NotFoundMapper implements ExceptionMapper<WHATEXCEPTION?> {
...
}
RestEasy has a proprietary NotFoundException, but is there a way that works with Jersey, too? Ideally a standard compliant way?
Do you mean if your rest service is listening under localhost/rest then localhost/rest/asdf should return you a customized 404 page? It is specific to the JAX-RS engine, you must check the source code of Jersey. For example CXF throws a WebApplicationException, but it does not provide any way to customize it. See JAXRSInInterceptor.processRequest() line 156 to see how it is done :
org.apache.cxf.common.i18n.Message errorMsg =
new org.apache.cxf.common.i18n.Message("NO_ROOT_EXC",
BUNDLE,
message.get(Message.REQUEST_URI),
rawPath);
LOG.warning(errorMsg.toString());
Response resp = JAXRSUtils.createResponse(resource, message, errorMsg.toString(),
Response.Status.NOT_FOUND.getStatusCode(), false);
throw new WebApplicationException(resp);
To summarize there is no nice and standard way, you can try to use a request filter or an ExceptionMapper