How to handle exception thrown by object in @Requestbody in spring boot 2
Asked Answered
M

4

6

I have a rest controller class as follows which throws custom exception when user object is invalid. But those exception got wrapped by spring framework and not handled by the specific exception handler defined in controller advice.

@PostMapping
public ResponseEntity<String> process(
  @PathVariable(User_id) String id,
  @Valid @RequestBody(required = false) User user) {
 }

For example, Json mapping exception got wrapped as org.springframework.http.converter.HttpMessageNotReadableException and not handled by following exception handler in controller advice.

@ExceptionHandler(JsonMappingException.class)
public ResponseEntity<ErrorMessage> handleJsonMappingException(JsonMappingException e){
//process to return error message
}

How to handle these specific exception thrown while building the request body objects?

Actual Exception received:

2018-09-16 23:14:07,671 ERROR [qtp1824013753-23] c.m.a.c.e.m.MyExceptionHandler [MyExceptionHandler.java:111] Unhandled exception : Type definition error: [simple type, class com.common.model.User]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.common.model.User`, problem: '123ghijk' is not a valid DeptName
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.common.model.Employee["DeptName"])
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.common.model.User]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.common.model.User`, problem: '123ghijk' is not a valid DeptName
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.common.model.Employee["DeptName"])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:238)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:223)
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:206)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:157)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:131)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:865)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1655)
    at com.gateway.rest.OutstandingCallFilter.doFilter(OutstandingCallFilter.java:84)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at com.common.logging.Slf4jRequestLoggingFilter.doFilter(Slf4jRequestLoggingFilter.java:30)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at com.gateway.rest.AuthenticationFilter.doFilter(AuthenticationFilter.java:40)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1642)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:533)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:146)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:257)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:255)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1317)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:203)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:201)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1219)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:144)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:531)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:310)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:168)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:126)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:366)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:762)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:680)
    at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.common.model.User`, problem: '123ghijk' is not a valid DeptName
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.common.model.Employee["DeptName"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1601)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:484)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:503)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:328)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
    at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3984)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2276)
    at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:2758)
    at com.common.json.ApiDeserializer.deserialize(ApiDeserializer.java:48)
    at com.common.json.ApiDeserializer.deserializeWithType(ApiDeserializer.java:32)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:68)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235)
    ... 70 common frames omitted
Caused by: com.common.exception.MyException: '123asd' is not a valid DeptName
    at com.common.model.User.stringToUser(User.java:41)
    at com.common.model.User.<init>(User.java:29)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call1(AnnotatedConstructor.java:129)
    at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:326)
Mantelpiece answered 17/9, 2018 at 9:43 Comment(0)
A
4

You should use @ExceptionHandler(MethodArgumentNotValidException.class) in a controllerAdvice.

@ControllerAdvice
public class ExceptionHandlingController {


    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorMessage> handleException(MethodArgumentNotValidExceptione e){

     //e.getBindingResult().getAllErrors()

     }

     @ExceptionHandler(JsonMappingException.class)
     public  
     ResponseEntity<ErrorMessage>handleJsonMappingException(JsonMappingException e){
     }

}
Allophane answered 17/9, 2018 at 9:53 Comment(2)
I have @ExceptionHandler(MethodArgumentNotValidException.class). But it doesn't helps. it flows to generic exception handler which was tagged as lowest precedence @ExceptionHandler(Exception.class)Mantelpiece
It only means that the Exception that happened is not of type MethodArgumentNotValidException. Log exception.toString() and see what type of Exception it is.Colorimeter
M
1

You can handle exceptions in Spring boot by method level, controller level or globally.

This is how you can handle all exceptions globally and send your customer Error Response.

Below you can handle all your method argument exceptions like all validations defined in your entity or business entity you define as @RequestBody but make sure you put @Valid before Body Request in your controller method.

same you can do for other type of exception, even your custom exceptions, Just extend by Type Of Exception and handle here in same @RestControllerAdvice

@RestControllerAdvice
public class GlobalExceptionHandler {

 @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorResponse processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        FieldError error = result.getFieldError();
        return processFieldError(error);
    }

 private ErrorResponse processFieldError(FieldError error) {
        ErrorResponse errorResponse = new ErrorResponse();
        if (error != null) {
            Locale currentLocale = LocaleContextHolder.getLocale();
            String msg = messageSource.getMessage(error.getDefaultMessage(), null, currentLocale);
            errorResponse.setMessage(msg);
        }
        return errorResponse;
    }
}
Mystical answered 17/9, 2018 at 12:11 Comment(0)
U
1

In your case, your exception is HttpMessageNotReadableException so you need to handle that exception:

@ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<ErrorMessage> handleException(HttpMessageNotReadableException e){

    //process to return error message

}
Utilitarian answered 1/7, 2021 at 7:17 Comment(0)
A
1

I tried to handle the exception as suggested in other answers but the application would not start. After investigating further, I realised how @ControllerAdvice handles this exception in the class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler: via the protected method protected ResponseEntity<Object> handleHttpMessageNotReadable( HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request). So what I did was to override the behaviour if this method in my controller advice class:

   ...

   protected ResponseEntity<Object> handleHttpMessageNotReadable(
    HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status,
    WebRequest request) {
    ...
    return new ResponseEntity<>("Payload not correctly defined", HttpStatus.BAD_REQUEST);
}
Aerial answered 13/10, 2021 at 20:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.