In our (legacy) codebase, we're throwing WebApplicationExceptions in different ways.
In an attempt to make some order in how we're handling exceptions - I wanted to create an ExceptionMapper for these WAEs (and others).
I realized, however, that Jersey's ExceptionMapper only maps WAE which weren't thrown with an entity.
For example:
throw new WebApplicationException(Response.status(500).build());
This exception is caught by the ExceptionMapper.
throw new WebApplicationException(Response.status(500).entity(WsResourceUtils.createWSResourceRestError(500, "bla")).build());
This exception is NOT caught by the ExceptionMapper.
Both are thrown from the same point in code.
This is my ExceptionMapper:
#Provider
public class GeneralExceptionMapper implements ExceptionMapper<Throwable> {
private static final Logger logger = LoggerFactory.getLogger(GeneralExceptionMapper.class);
#Override
public Response toResponse(Throwable e) {
logger.error("Caught a WAE", e);
...
}
Is it possible to create an ExceptionMapper which will catch WebApplicationExceptions even if their response is already built with an entity?
We're using Jersey 1.17.
Thanks.
This behaviour is describe in the JAX-RS specification and it's still true in version 3.0 :
Instances of WebApplicationException and its subclasses MUST be mapped to a response as follows. If the response property of the exception does not contain an entity and an exception mapping provider (see Exception Mapping Providers) is available for WebApplicationException or the corresponding subclass, an implementation MUST use the provider to create a new Response instance, otherwise the response property is used directly. The resulting Response instance is then processed according to Return Type.
Source : https://jakarta.ee/specifications/restful-ws/3.0/jakarta-restful-ws-spec-3.0.html#method_exc
I don't know why and I think it's not ideal but it's definitely not a bug.
please consider looking at the code in GIST :
https://gist.github.com/jeorfevre/9fb2c447a01bcc724998
Register a Mapper that of MagicException that contains Response
Define Exception that contain response
Throw this exception
Related
I implement a service provider for an existing WSDL with Spring Boot. The WSDL specifies a service with an additional fault message. The corresponing fault info has some details including a timestamp, the class of the causing exception and its stack trace as well as information contained in the original request. All of the details are defined as XML elements on its own.
These information are available at runtime when the service is executed at the server. If an error occurs then an appropriate exception is thrown which contains these information.
With spring boot one can configure an instance of org.springframework.ws.server.EndpointExceptionResolver
to map exceptions to fault infos. However, it seems that in all its implementing classes it is only possible to add a fault info message and a fault code. I did not find a way to add a structured object or better: an object for which a JAXB serialization is defined.
How is this possible?
Sure that's not a problem.
One way could be to create a custom SoapFaultMappingExceptionResolver that maps the exception to the fault:
public class DetailSoapFaultDefinitionExceptionResolver extends SoapFaultMappingExceptionResolver {
private static final QName CODE = new QName("code");
private static final QName DESCRIPTION = new QName("description");
#Override
protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
logger.warn("Exception processed ", ex);
if (ex instanceof ServiceFaultException) {
ServiceFault serviceFault = ((ServiceFaultException) ex).getServiceFault();
SoapFaultDetail detail = fault.addFaultDetail();
detail.addFaultDetailElement(CODE).addText(serviceFault.getCode());
detail.addFaultDetailElement(DESCRIPTION).addText(serviceFault.getDescription());
}
}
}
Please find a complete example here:
https://memorynotfound.com/spring-ws-add-detail-soapfault-exception-handling/
I'm wondering if there is any elegant way to catch all exceptions (specifically custom runtime exceptions) and return an exception containing a list of the messages.
Instead of having a String message, the big exception would then contain String[] message for example.
Scenario:
A REST request is made to the back-end with a JSON object containing a bunch of fields. I want to validate these fields on the backend and return a list of errors if any exceptions occur.
If both the name and lastname field are not acceptable input, I don't want to throw an exception on the invalid name and have the user change the name and submit again only to get an error message that the lastname is invalid too.
Hence why I want to collect all invalid input and return a list of these in the form of an exception.
Spring collects JSR-303/JSR-349 bean validation failures into a BindException:
Thrown when binding errors are considered fatal. Implements the BindingResult interface (and its super-interface Errors) to allow for the direct analysis of binding errors.
Instead of developing your own mechanism for bean validation you might want to read 3. Validation, Data Binding, and Type Conversion and follow the standards.
With Spring Boot, you can use the following annotation to handle any kind of Exception for a class or a method :
#ExceptionHandler(YourExceptionHandler.class)
And you can create a class that let you regroup all your custom exception management like this (if you want to gather it) :
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.CONFLICT) // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
You can also implement the interface HandlerExceptionResolver to manage all Exceptions that ARE NOT handled by the Controllers (all the others runtime Exceptions)
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex);
}
Everything is explained in details here : https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
EDIT: I just read that you added up scenario. Actually, for your special case, you should just parse the object, and return one exception (like bad object format, along with a 400 HTTP status code error, with a custom message containing all the fields that are invalid. I guess.
I have a controller class with REST methods that can throw various exceptions. I have decided to handle these exceptions in a separate class using the #ControllerAdvice and #ExceptionHandler for my handler methods.
However, I have the problem, that my REST methods use an annotation from another library. This library catches an exception that my REST method throws as well.
Now that I am handling the exceptions globally and not via try/catch in the REST method directly, my exception is always caught by the other library and not by my own handler method. Apparently, that other method in the library I am using wins due to the annotation.
How can I bind the exception handling to my own class to prevent it from being caught by anyone else?
My REST method:
#SomeLibraryAnnotation
#PostMapping(path = "/add", consumes = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity< ? > addItem(#RequestHeader HttpHeaders headers, #RequestBody MyDTO myDTO)
throws UnsupportedOperationException {
doSomethingWith(myDTO);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
My Exception Handler class:
#ControllerAdvice
public class MyExceptionHandler {
#ExceptionHandler(UnsupportedOperationException.class)
public ResponseEntity<?> handleUnsupportedOperationException(UnsupportedOperationException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}
}
As the library method also catches the UnsupportedOperationException, it wins due to the #SomeLibraryAnnotation and the exception is never handled in my handler class.
You might try using #Order or #Priority in the MyExceptionHandler class, as discussed in Setting Precedence of Multiple #ControllerAdvice #ExceptionHandlers.
That would give Spring an opportunity to use your class instead of the one specified by the #SomeLibraryAnnotation. However, without knowing how Spring interprets that other annotation at context initialization, that's just a guess.
Did you tried to write #ExceptionHandler inside your controller? Like:
#RestController
#RequestMapping("/path")
public class TheController {
#ExceptionHandler(UnsupportedOperationException.class)
public ResponseEntity<?> handleUnsupportedOperationException(UnsupportedOperationException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
}
}
Maybe that would pickup and exception with higher priority. It's hard to answer not knowing what #SomeLibraryAnnotation is...
Those are simply Java Language rules, i.e. exception is no longer unhandled, as it was handled (caught in a catch block) by your other library. What you can do it is to re-throw (maybe conditionally) another exception in your library which caught original exception and see if #ExceptionHandler will handle it. It might not because #ExceptionHandler is handling exceptions thrown in Controller classes.
Second approach would be to throw exception which is only handled in #ExceptionHandler and then re-throw it be handled in other library.
In other words you need to choose where to handled first originally thrown exception.
Third approach would be use AOP interceptor #AfterThrowing or #Around and then execute whatever logic you want within.
Essence : There is no way to handle exception in two places at one time. Does it make sense?
I am working on an application which uses Dropwizard, which has this implementation of ExceptionMapper: https://github.com/dropwizard/dropwizard/blob/master/dropwizard-jersey/src/main/java/io/dropwizard/jersey/errors/LoggingExceptionMapper.java
Problem with this implementation is that even though this catches both 4** and 5** errors, it only logs 5** errors.
I need to implement ExceptionMapper such that LoggingExceptionMapper is not used at all and my CustomExceptionMapper logs both CLIENT_ERRORs and SERVER_ERRORs.
I am wondering how would my application know that it needs to use CustomExceptionMapper instead of the Dropwizard class?
Also would it suffice to add CLIENT_ERROR to if condition, to log out all errors?
#Override
public Response toResponse(E exception) {
// If we're dealing with a web exception, we can service certain types of request (like
// redirection or server errors) better and also propagate properties of the inner response.
if (exception instanceof WebApplicationException) {
final Response response = ((WebApplicationException) exception).getResponse();
Response.Status.Family family = response.getStatusInfo().getFamily();
if (family.equals(Response.Status.Family.REDIRECTION)) {
return response;
}
if (family.equals(Response.Status.Family.SERVER_ERROR) || family.equals(Response.Status.Family.CLIENT_ERROR) {
logException(exception);
}
return Response.fromResponse(response)
.type(MediaType.APPLICATION_JSON_TYPE)
.entity(new ErrorMessage(response.getStatus(), exception.getLocalizedMessage()))
.build();
}
Or would there be a better way to do this?
JAX-RS spec about ExceptionMapper:
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.
How would my application know that it needs to use CustomExceptionMapper instead of the Dropwizard class?
You can throw a custom exception from your application and create an ExceptionMapper for that specific exception.
Would it suffice to add CLIENT_ERROR to if condition, to log out all errors?
Yes, 4xx and 5xx family has all the error responses.
Is there a way to have global exception handling in Jersey? Instead of individual resources having try/catch blocks and then calling some method that then sanitizes all of the exceptions to be sent back to the client, I was hoping there was a way to put this where the resources are actually called. Is this even possible? If so, how?
Instead of, where sanitize(e) would throw some sort of Jersey-configured exception to the Jersey servlet:
#GET
public Object getStuff() {
try {
doStuff();
} catch (Exception e) {
ExceptionHandler.sanitize(e);
}
}
Having:
#GET
public Object getStuff() throws Exception {
doStuff();
}
where the exception would get thrown to something that I can intercept and call sanitize(e) from there.
This is really just to simplify all the Jersey resources and to guarantee that the exceptions going back to the client are always in some sort of understandable form.
Yes. JAX-RS has a concept of ExceptionMappers. You can create your own ExceptionMapper interface to map any exception to a response. For more info see: https://jersey.github.io/documentation/latest/representations.html#d0e6352
javax.ws.rs.ext.ExceptionMapper is your friend.
Source: https://jersey.java.net/documentation/latest/representations.html#d0e6665
Example:
#Provider
public class EntityNotFoundMapper implements ExceptionMapper<javax.persistence.EntityNotFoundException> {
public Response toResponse(javax.persistence.EntityNotFoundException ex) {
return Response.status(404).
entity(ex.getMessage()).
type("text/plain").
build();
}
}
All the answers above are still valid. But with latest versions of spring Boot consider one of below approaches.
Approach 1 :
#ExceptionHandler- Annotate a method in a controller with this annotation.
Drawback of this approach is we need to write a method with this annotation in each controller.
We can work around this solution by extending all controllers with base controller (that base controller can have a method annotated with #ExceptionHandler. But it may not be possible all the times.
Approach 2 :
Annotating a class with #ControllerAdvice and define methods with #ExceptionHandler
This is similar to Controller based exception (refer approach 1) but this is used when controller class is not handling the exception.
This approach is good for global handling of exceptions in Rest Api