#ExceptionHandler is Not working when automatic binding fails in REST API - spring-boot

I have two REST API's GET POST
When any Exception is thrown inside the method, Exception handler is working fine.
But if i use malformed REST api uri then it only shows 400 Bad Request without going to Exception Handler.
Eg.
If I hit http://localhost:8080/mypojoInteger/abc, it fails to parse string into Integer and hence I am expecting it to go to ExceptionHandler.
It does not go to Exception Handler, Instead I only see 400 Bad Request.
It works fine and goes to Exception Handler when any Exception is thrown inside the GET/POST method.
For eg: It works fine and goes to Exception Handler if I use 123 in path variable
http://localhost:8085/mypojoInteger/123
And change getData method to
#GetMapping("/mypojoInteger/{sentNumber}")
public void getData(#PathVariable("sentNumber") Integer sentNumber) {
throw new NumberFormatException("Exception");
}
NOTE: Same issue is with POST request also.
GET:
#GetMapping("/mypojoInteger/{sentNumber}")
public void getData(#PathVariable("sentNumber") Integer sentNumber) {
//some code
}
POST:
public void postData(#RequestBody MyPojo myPojo) {
//some code
}
Controller Advice class:
#ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(NumberFormatException.class)
protected ResponseEntity<Object> handleEntityNotFound(
NumberFormatException ex) {
// some logic
}
}
How can I handle Exception when it fails to bind String to Integer in REST API uri itself??
EDIT: My Requirement is I should handle the overflow value of integer i.e, If a pass more than maximum value of Integer it must handle it rather than throwing NumberFormatException Stack Trace.
Eg: When i pass over flow value
POJO:
public class MyPojo extends Exception {
private String name;
private Integer myInt;
//getters/setter
}
{
"name":"name",
"myInt":12378977977987879
}
Without #ControllerAdvice it just shows the NumberFormatException StackTrace.
With #ControllerAdvice it just shows 400 bad request with no Response Entity.
I do not want this default stacktrace/400 bad request in case of this scenario
but I want to show my custom message.

The reason that i see is that, because since your request itself is malformed-> the method body never gets executed - hence the exception never occurs because it is only meant to handle the error within the method . It is probably a better design choice for you to form a proper request body rather than allowing it to execute any method so you know the problem before hand.

The issue is because Integer object is not sent as a valid request parameter, example of request: 5 if you send String an exception will be thrown directly. If you want to check if it is a String or Integer you might change your code by following this way:
#GetMapping("/mypojoInteger/{sentNumber}")
public void getData(#PathVariable("sentNumber") Object sentNumber) {
if (!(data instanceof Integer)) {
throw new NumberFormatException("Exception");
}
}
This should work on your example.

Solution:
I found out that I need to handle Bad Request.
So, I have override
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
//Handle Bad Request
}

Related

In ResponseEntityExceptionHandler what is the exact difference between handleExceptionInternal and handleException?

I am implementing all the methods of ResponseEntityExceptionHandler because i don't want Spring to send any standard error responses towards the client. There are two seemingly similar methods that confuse me a bit. Namely handleExceptionInternal and handleException. These are the definitions of both methods according to the official documentation.
handleException(Exception ex, WebRequest request)
Provides handling for standard Spring MVC exceptions.
handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request)
A single place to customize the response body of all exception types.
I find these explanations a bit vague. What can be considered 'standard spring mvc exceptions' for example? And should handleExceptionInternal be considered like a 'default' handler method that is used when none of the other methods can catch the spring exception? Please correct me if i'm wrong.
Thank you
handleException method is a common exception handler for standard spring mvc exceptions. Its main task is it maps these exception to respective status code as per http response code convention, which most likely you are not going to change.
e.g.
HttpRequestMethodNotSupportedException -> 405
HttpMediaTypeNotSupportedException -> 415
NoHandlerFoundException -> 404
All these exceptions are handled in their specific handler methods handle{ExceptionName} so that for some reason, if you want to change the status code (or add response body for detailed info), you can do so by overriding specific handler. All these handlers further delegate to handleExceptionInternal.
One thing you have noticed each handle{ExceptionName} methods pass body as null to handleExceptionInternal. These methods just return the status code with no body which doesn't give you more details about the error.
A common practice is to return a custom error response body with details so that your api consumers know the exact error cause. This is the place you can inject your custom body by creating an Error object. A simple error message would look like.
public class ApiError {
private final int status;
private final int message;
public ApiError(int status, int message) {
this.status = status;
this.message = message;
}
// getters
}
And you can override handleExceptionInternal method as:
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
ApiError error = new ApiError(status.value(), ex.getMessage());
return super.handleExceptionInternal(ex, error, headers, status, request);
}
Summary
If handleException wouldn't there, then you need to manually map each exceptions to respective error code. If handleExceptionInternal were missing then to inject error body you would need to override each handle{Exception} methods.
Update
RFC for http status code definition.

How to check Bad request for #Min on request param in spring controller?

I am pretty new to spring controller. I am trying to write unit test for invalid parameter. I have an api that has #RequestParam("id") #Min(1) long id and in my unit test, I pass in "-1". Here is my test:
#Test
public void searchWithInvalidIbId() throws Exception {
mockMvc.perform(get(BASE_URL)
.param(COLUMN_IB_ID, INVALID_IB_ID_VALUE) // = "-1"
.param(COLUMN_TIME_RANGE, TIME_RANGE_VALUE)
.param(COLUMN_TIME_ZONE, TIME_ZONE_VALUE)
.accept(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON)
.contentType(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON))
.andExpect(status().isBadRequest());
}
When I run this, I get
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: search.arg2: must be greater than or equal to 1
It makes sense, but I am not sure how to test this is BadRequest. I tried #Test(expected = NestedServletException.class), and it passed, but I don't think it is checking what I want to check. What is the right approach to check this?
You can have your custom exception handler annotated with #ControllerAdvice and handle ConstraintViolationException in that class. You can throw your custom exception with additional details if you wish.
Here is an example approach:
#ControllerAdvice
public class MyCustomExceptionHandler {
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
ApiError constraintViolationException(ConstraintViolationException e) {
return BAD_REQUEST.apply(e.getBindingResult());
}
}
Here ApiError is a custom class to represent your error response, it can be anything else you want. You can add timestamp, http status, your error message etc.

Produce a JSONP with a ContainerRequestContext#abortWith

I have this Jersey2-based application, with a custom ContainerRequestFilter.
When the filter(ContainerRequestContext) method is called I want to do a check and, if needed, I want to be able to stop the request before entering the main logic of the application.
At the moment I'm using the ContainerRequestContext#abortWith method to block the call and return an "error" response to the client.
My application returns JSONP to the client, and if I block with abortWith the response is always a JSON.
Looking at the jersey sources I found
org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor that is responsible of the JSONP serialization.
In the abortWith flow I see it fails to find the JSONP annotation, but I don't know where it search for it.
My method has it, in fact in the "normal" scenario (without the abortWith) I see correctly the JSONP format.
I found the solution.
The ContainerRequestFilter#filter method was something like
public void filter(final ContainerRequestContext crc) throws IOException {
if (/* logic */) {
CustomObject ret = new CustomObject();
ret.error = "error message";
crc.abortWith(Response.ok(ret)).build());
}
}
JsonWithPaddingInterceptor expected a response with a JSONP annotation so I retrieve them from the ResourceInfo#resourceMethod, with something like
public void filter(final ContainerRequestContext crc) throws IOException {
if (/* logic */) {
Annotation[] as = this.resourceInfo.getResourceMethod().getAnnotations();
CustomObject ret = new CustomObject();
ret.error = "error message";
crc.abortWith(Response.ok().entity(ret, as).build());
}
}
this way the annotation is correctly found

Can I get Spring Validation errors in prehandle

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
}
}

How to handle Exception occuring when returning StreamingResponseBody from RestController

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.

Resources