如何处理spring boot 2中@Requestbody中的对象抛出的异常

Posted

技术标签:

【中文标题】如何处理spring boot 2中@Requestbody中的对象抛出的异常【英文标题】:How to handle exception thrown by object in @Requestbody in spring boot 2 【发布时间】:2019-02-21 04:59:08 【问题描述】:

我有一个如下的休息控制器类,当用户对象无效时它会抛出自定义异常。但是这些异常被 Spring 框架包装,而不是由控制器建议中定义的特定异常处理程序处理。

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

例如,Json 映射异常被包装为 org.springframework.http.converter.HttpMessageNotReadableException 并且不被控制器建议中的以下异常处理程序处理。

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

如何处理构建请求体对象时抛出的这些特定异常?

收到的实际异常:

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)

【问题讨论】:

【参考方案1】:

我尝试按照其他答案中的建议处理异常,但应用程序无法启动。在进一步调查之后,我意识到@ControllerAdvice 是如何在org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler 类中处理这个异常的:通过受保护的方法protected ResponseEntity&lt;Object&gt; handleHttpMessageNotReadable( HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request)。所以我所做的是在我的控制器建议类中覆盖此方法的行为:

   ...

   protected ResponseEntity<Object> handleHttpMessageNotReadable(
    HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status,
    WebRequest request) 
    ...
    return new ResponseEntity<>("Payload not correctly defined", HttpStatus.BAD_REQUEST);

【讨论】:

【参考方案2】:

在您的情况下,您的例外是HttpMessageNotReadableException 所以你需要处理那个异常:

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

    //process to return error message


【讨论】:

【参考方案3】:

你可以通过method level, controller level or globally处理Spring boot中的异常。

这是您可以在全球范围内处理所有异常并向您的客户发送错误响应的方式。

您可以在下面处理所有方法参数异常,例如定义为 @RequestBody 的实体或业务实体中定义的所有验证,但请确保在控制器方法中将 @Valid 放在 Body Request 之前。

你可以对其他类型的异常做同样的事情,甚至是你的自定义异常,只需按异常类型扩展并在同一 @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;
    

【讨论】:

【参考方案4】:

您应该在controllerAdvice 中使用@ExceptionHandler(MethodArgumentNotValidException.class)

@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)
     


【讨论】:

我有@ExceptionHandler(MethodArgumentNotValidException.class)。但这没有帮助。它流向被标记为最低优先级的通用异常处理程序 @ExceptionHandler(Exception.class) 只表示发生的异常不是MethodArgumentNotValidException类型。记录 exception.toString() 并查看它是什么类型的 Exception。

以上是关于如何处理spring boot 2中@Requestbody中的对象抛出的异常的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 Spring Boot 重定向到 /error?

如何处理 Spring Boot、AngularJS 应用程序中的 CORS 错误?

Spring Boot 如何处理可抛出对象

spring boot中各种数据不匹配如何处理jackson反序列化错误

Spring Boot 如何处理 Hibernate 会话?

如何处理 Spring Boot Rest 和 Jackson Deserializer 的 Javax 验证中的无效数字