Our spring boot controllers have methods called by ajax as well as the standard methods rendering CRUD templates. We'd like to be able to annotate all our ajax methods with a single annotation so that regardless of what type of exception is thrown, we can return a response the ui can handle.
I've been looking at the ControllerAdvice and ExceptionHandler annotations, but I don't think either can be used the way we intend. ControllerAdvice can only cover entire controllers, so any exception handling would also cover the non-ajax methods. Similarly, the ExceptionHandler annotation would handle exceptions from both types of methods.
The current idea is to split the two types of methods, ajax and CRUD, into separate controllers. Is there another way to do this?
I don't think this is that much easy/straightforward in Spring (or at least to my current spring knowledge).
We'd like to be able to annotate all our ajax methods with a single
annotation
Apart from separating ajaxMethods into another controller, I can think of another way of doing this.
1.Declare your own exception class like myAjaxException.
2.Don't handle this exception in controller, just throw it when you encounter any exception inside your ajax methods.
try{
//....
}catch(Exception ex){
throw new myAjaxException("this is myAjaxException blah blah blah");
}
3.Intercept myAjaxException by implementing HandlerExceptionResolver as below.
Any Spring bean declared in the DispatcherServlet’s application
context that implements HandlerExceptionResolver will be used to
intercept and process any exception raised in the MVC system and not
handled by a Controller.
public class AjaxExceptionResolver implements HandlerExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
if (ex instanceof myAjaxException) {
//return response to UI
}
}
Related
I have a method annotated with #PreAuthorize(...) with some logic that goes away and queries an API for some information about what the user can view. However, I have this endpoint that I need to add this #PreAuthorize annotation into which receives in a more "complex" object which I want to transform slightly (the object contains an array that is some cases I want to add/remove data from).
#PostMapping("/search")
#PreAuthorize("#Service.isAuth(#searchParam)")
public ResponseEntity<Response> search(SearchParams searchParam) {
return service.getSearchResult(searchParam);
}
Is there a way I can modify searchParam inside the #PreAuthorize annotation then have it passed into the method body, I know that this is probably is not the correct way to do this and maybe isn't something that #PreAuthorize wasn't designed for but is there any way of doing this even with a different type of annotation. Obviously worst case I can move the logic into the method body but I would prefer to use an annotation-based solution like #PreAuthorize offers if possible. Thanks for any help even links to other relevant things would be useful I've not found much on google related to this.
I think the best solution is to make a handler/interceptor and then annotate it with #PreAuthorize. So I think you are in the right track but you need to make sure that you modify your code to implement the HandlerMapping interface to create the interceptor and then override the prehandle method. After you need to annotate it with #PreAuthorize programatically. The last thing will be to use a wrapper to modify the HttpWrapper, it cannot be done manually. Here links to the relevant resources in order:
Creating a Handler/Interceptor: https://www.baeldung.com/spring-mvc-handlerinterceptor
Using PreAuthorise in the interceptor: How can I require that all request handlers in my Spring application have #PreAuthorize
To modify the HttpServlet request you will need a wrapper: How to modify HttpServletRequest body in java?
Have a try, hopefully that works.
Snippet of code taken from second link uses a programatic PreAuthorize rather than annotation:
public class PreAuthorizeChecker implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
PreAuthorize annotation = AnnotationUtils.findAnnotation(hm.getMethod(), PreAuthorize.class);
//TODO use the technique shown on the third link to wrap and modify the HttpServletRequest
if (annotation == null) {
// prevent access to method wihout security restrictions
throw new RuntimeException("Rights are not defined for this handler");
}
}
return true;
}
.....
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?
Is there a way to somehow globally handle unchecked exceptions in osgi (karaf) that were thrown in servlets?
What I mean is something like in Spring, where there is #ControllerAdvice where you can specify methods for each exception type and handle it.
I would like to unify the exception handling in my rest api that exposes osgi services.
Doing REST in OSGi
You mention REST and Servlets in this question. If you're using REST in OSGi then the JAX-RS Whiteboard is the easiest way to do things. If you want to use raw Servlets, then the Http Whiteboard is the way to go. Both models make it easy to handle exceptions.
Update
In an effort to make it easier for people to see how this works, I've created a working example on GitHub which covers Servlets and JAX-RS error handling.
Using the HTTP Whiteboard
The HTTP whiteboard allows servlets to be registered as OSGi services and then used to handle requests. One type of request handling is to act as an error page.
Error pages are registered with the osgi.http.whiteboard.servlet.errorPage property. The value of this property is one or more Strings containing either:
A fully qualified class name for an exception that should be handled
A three digit error code
The OSGi specification describes this in an example, and other pages list the attributes that you can use to work out what went wrong.
For example this servlet will be called for IOException, NullPointerException and for status codes 401 and 403:
#Component
#HttpWhiteboardServletErrorPage(errorPage = {"java.io.IOException", "java.lang.NullPointerException", "401", "403"})
public class MyErrorServlet extends HttpServlet implements Servlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws Exception {
Throwable throwable = (Throwable) request
.getAttribute("javax.servlet.error.exception");
Integer statusCode = (Integer) request
.getAttribute("javax.servlet.error.status_code");
// Do stuff with the error
}
}
N.B. I have used OSGi R7 Component Property Type annotations to make this simpler to read. It will work with older versions of DS and the Http Whiteboard equally well.
Using the JAX-RS whiteboard
The JAX-RS Whiteboard allows you to use any of the JAX-RS extension types as a whiteboard service. In this case you want an ExceptionMapper.
In this example we add a handler for IOException
#Component
#JaxrsExtension
public class MyExceptionMapper implements ExceptionMapper<IOException> {
Response toResponse(IOException e) {
// Create a response
}
}
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