I currently have something similar to this in all of my endpoints in my spring app.
if(bindingResult.hasErrors()){
return new ResponseEntity<>(BAD_REQUEST);
}
I would like to move this to a http interceptor so that I only need it in one place. However, I cannot figure out how to get all of the errors from the binding result in preHandle.
How would I get validation errors in preHandle, or some other time before it starts the actual route?
One way to achieve what I think you're looking for is to not include BindingResult as a method parameter. Given no BindingResult is included as a method argument Spring will throw a BindException exception. You can define an ExceptionHandler, generally I've placed these within a #ControllerAdvice, to handle the exception as needed. Below is some sample code
Controller
#PostMapping
public SomeReturnObject someMethod(#Valid SomeCommand command) {
// logic - no longer contains checks for binding result errors
}
As part of ControllerAdvice
#ControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class ApplicationControllerAdvice {
....
#ExceptionHandler(BindException.class)
#ResponseBody
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
protected SomeResponse handleBindException(BindException ex) {
// handle exception
}
}
Related
I'm facing an issue with Spring (and kotlin?), where my global error handlers do not catch any exceptions thrown within a custom converter.
I know spring supports string->UUID mapping by default, but I wanted to explicitly check if an exception is actually thrown. Which it is the following converter. The behaviour is the same with and without my own implementation of the converter.
My WebMvcConfuguration looks as follows:
#Configuration
class WebMvcConfiguration : WebMvcConfigurerAdapter() {
override fun addFormatters(registry: FormatterRegistry) {
super.addFormatters(registry)
registry.addConverter(Converter<String, UUID> { str ->
try {
UUID.fromString(str)
} catch(e: IllegalArgumentException){
throw RuntimeException(e)
}
})
}
And this is my GlobalExceptionHandler:
(it also contains other handlers, which I ommitted for brevity)
#ControllerAdvice
class GlobalExceptionHandler : ResponseEntityExceptionHandler() {
#ExceptionHandler(Exception::class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
fun handleException(ex: Exception): ApiError {
logger.info(ex.message, ex)
return ApiError(ex.message)
}
}
And finally, the controller:
#Controller
class MyController : ApiBaseController() {
#GetMapping("/something/{id}")
fun getSomething(#PathVariable("id") id: UUID) {
throw NotImplementedError()
}
}
Exceptions inside controller (for example the NotImplementedError) methods are caught just fine. But the IllegalArgumentException thrown within the converter when invalid UUIDs are passed is swallowed, and spring returns an empty 400 response.
My question now is: How do I catch these errors and respond with a custom error message?
Thanks in advance!
I had the same problem. Spring swallowed any IllegalArgumentException (ConversionFailedException in my case).
To get the behavior i was looking for; i.e. only handling the listed exceptions and using default behavior for the other ones, you must not extend the ResponseEntityExceptionHandler.
Example:
#ControllerAdvice
public class RestResponseEntityExceptionHandler{
#ExceptionHandler(value = {NotFoundException.class})
public ResponseEntity<Object> handleNotFound(NotFoundException e, WebRequest request){
return new ResponseEntity<>(e.getMessage(), new HttpHeaders(), HttpStatus.NOT_FOUND);
}
}
I checked the solution from #georg-moser. At first, it looks good, but it looks it contains another issue. It translates all exceptions to the HTTP code of 500, which is something one not always wants.
Instead, I decided to overwrite the handleExceptionInternal method from the ResponseEntityExceptionHandler.
In my case logging the error was enough, so I ended up with the following:
#Override
#NonNull
protected ResponseEntity<Object> handleExceptionInternal(#Nonnull final Exception e,
final Object body,
final HttpHeaders headers,
final HttpStatus status,
#Nonnull final WebRequest request) {
final ResponseEntity<Object> responseEntity = super.handleExceptionInternal(e, body, headers, status, request);
logGenericException(e);
return responseEntity;
}
I hope it helps!
After some more trial and error, I have found a solution:
Instead of using #ControllerAdvice, implementing a BaseController that others inherit from and adding the exception handlers there works.
So my Base controller looks like this:
abstract class ApiBaseController{
#ExceptionHandler(Exception::class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
fun handleException(ex: Exception): ApiError {
return ApiError(ex.message)
}
}
If anyone can elaborate on why it works like this and not the other way, please do so and I will mark your answer as accepted.
I have implemented a Spring Rest Controller that streams back large files using the StreamingResponseBody. However, these files are coming from another system and there is the potential for something to go wrong while streaming them back. When this occurs I am throwing a custom Exception (MyException). I am handling the exception in an #ExceptionHandler implementation which is below. I am attempting to set the response httpstatus and error message but I am always receiving http status 406. What is the proper way to handle errors/exceptions while returning a StreamingResponseBody?
#ExceptionHandler(MyException.class)
public void handleParsException( MyException exception, HttpServletResponse response) throws IOException
{
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());
}
You should handle all errors in the same way. There are many options.
I prefer next:
Controller Advice
It is a good idea to have an entity to send a generic error response, an example:
public class Error {
private String code;
private int status;
private String message;
// Getters and Setters
}
Otherwise, to handle exceptions you should create a class annotated with #ControllerAdvice and then create methods annotated with #ExceptionHandler and the exception or exceptions (it could be more than one) you want to handle. Finally return ResponseEntity<Error> with the status code you want.
public class Hanlder{
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleResourceNotFoundException(MyException
myException, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.CONFLICT.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.CONFLICT);
}
#ExceptionHandler({DataAccessException.class, , OtherException.class})
public ResponseEntity<?> handleResourceNotFoundException(Exception
exception, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.INTERNAL_ERROR.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.INTERNAL_ERROR);
}
}
Other ways:
Annotate exception directly
Other way is annotating directly the excetion with the status and the reason to return:
#ResponseStatus(value=HttpStatus.CONFLICT, reason="Error with StreamingResponseBody")
public class MyError extends RuntimeException {
// Impl ...
}
Exception Handler in a specific controller
Use a method annotated with #ExceptionHandler in a method of a #Controller to handle #RequestMapping exceptions:
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Error with StreamingResponse Body")
#ExceptionHandler(MyError.class)
public void entitiyExists() {
}
I figured the problem out. The client was only accepting the file type as an acceptable response. Therefore, when returning an error in the form of an html page I was getting httpstatus 406. I just needed to tell the client to accept html as well to display the message.
I recently start to work with a #ControllerAdvice class to manage the exceptions in my Spring project. My current implementation is something like this:
#ControllerAdvice
public class GlobalDefaultExceptionHandler {
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) throw e;
return new ModelAndView("error/5xx", "exception", e);
}
}
My next step should be handle more exceptions, but for this I am thinking of use multiple classes with #ControllerAdvice, one for http status code. My goal is make the methods of my controller which handle the form submissions redirect the user for some of my custom status pages (I have one for each group - 1xx, 2xx, 3xx, 4xx, 5xx).
That methods have a structure similar to this:
#RequestMapping(value="cadastra")
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
public String cadastra(Model model) throws InstantiationException, IllegalAccessException {
model.addAttribute("command", this.entity.newInstance());
return "private/cadastrar";
}
Anyone can tell me if this is a good approach and give some hint of how implement my controller methods to accomplish what I want?
You can have multiple #ControllerAdvice classes that handle different exceptions.
However, because you are handling the Exception.class on your GlobalDefaultExceptionHandler, any exception might be swallowed by it.
The way I got around this was to add #Order( value = Ordered.LOWEST_PRECEDENCE )
on my general exception handler and #Order( value = Ordered.HIGHEST_PRECEDENCE ) on the others.
Maybe you want to define specific exception classes (thrown by your controller, for example: NoResourceFoundException or InvalidResourceStatusException and so on) so your ExceptionController can seperate the different cases and redirect them to the proper status page.
I am using spring MVC 3.
I validate various users input and show errors as applicable.
But this often to show the spring errors like org.springframework.core.convert.ConversionFailedException etc being shown on UI. How can i prevent the output of these errors on webpage ?
Note:
I understand that for topic starter my answer may be no longer
relevant. But it can be useful for those who have visited this page to
search for solutions to similar problems.
Answer:
In order to prevent the output of errors on web page, you may handle them. There are several types of error handling, that you may use for this in Spring MVC 3.x and above:
Controller-based exception handling
Global exception handling
Other methods, that are bit more complicated
Controller-based exception handling
You can add an #ExceptionHandler annotation on methods inside a controller. Such methods will function as error handlers for exceptions thrown from methods annotated as #RequestMapping in the same controller.
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception e) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("exception", e);
modelAndView.addObject("url", req.getRequestURL());
modelAndView.setViewName("error");
return modelAndView;
}
Global exception handling
A controller advice allows you to apply exception handling across the whole application, not just to an individual controller. In other words, handling will apply to exceptions thrown from any controller.
#ControllerAdvice
class GlobalControllerExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(Exception.class)
public void handleError(HttpServletRequest req, Exception e) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("exception", e);
modelAndView.addObject("url", req.getRequestURL());
modelAndView.setViewName(DEFAULT_ERROR_VIEW);
return modelAndView;
}
}
For more info:
Exception Handling in Spring MVC
I want to handle exceptions so the URL information is automatically shown to the client. Is there an easy way to do this?
<bean id="outboundExceptionAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<!-- what property to set here? -->
</bean>
You have two choices:
Spring Reference 15.9.1 HandlerExceptionResolver
Spring HandlerExceptionResolvers ease the pain of unexpected
exceptions that occur while your request is handled by a controller
that matched the request. HandlerExceptionResolvers somewhat resemble
the exception mappings you can define in the web application
descriptor web.xml. However, they provide a more flexible way to
handle exceptions. They provide information about which handler was
executing when the exception was thrown. Furthermore, a programmatic
way of handling exceptions gives you more options for responding
appropriately before the request is forwarded to another URL (the same
end result as when you use the servlet specific exception mappings).
The HandlerExceptionResolver has one method, containing everything you need:
HandlerExceptionResolver.resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex)
Or if you need different handlers for different controllers: Spring Reference Chapter 15.9.2 #ExceptionHandler
#ExceptionHandler(IOException.class)
public String handleIOException(IOException ex, HttpServletRequest request) {
return "every thing you asked for: " + request;
}
Short question short answer
I'm doing the following trick:
#ExceptionHandler(Exception.class)
public ModelAndView handleMyException(Exception exception) {
ModelAndView mv = new ModelAndView("redirect:errorMessage?error="+exception.getMessage());
return mv;
}
#RequestMapping(value="/errorMessage", method=RequestMethod.GET)
#Responsebody
public String handleMyExceptionOnRedirect(#RequestParamter("error") String error) {
return error;
}
Works flawless.