代码之家  ›  专栏  ›  技术社区  ›  Yuriy Tsarkov

在ControllerAdvice之前捕获反序列化异常

  •  0
  • Yuriy Tsarkov  · 技术社区  · 7 年前

    这里有一个问题:我有一个采用输入模型的控制器。比如说

    public class AppUserUpdateData {
    
      @NotNull
      @Size(min = 1, max = 50)
      protected String login;  
      @JsonDeserialize(using = MyDateTimeDeserializer.class)  
      protected Date startWorkDate;
      *************
      other properties and methods
      *************
    }
    

    问题是,当我想要限制一个日期板时,我最终得到一个HTTP异常400,没有任何消息,尽管我在代码中处理了这个案例! 这里是一个控制器:

     @RequestMapping(
          value = "/users/{userId}", method = RequestMethod.PUT,
          produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
      public @ResponseBody AbstractSuccessResult updateUser(@PathVariable Long userId,
          @RequestBody AppUserUpdateData  appUserUpdateRequest, HttpServletRequest request) {    
        AbstractSuccessResult response = new AbstractSuccessResult();
        appUserService.updateUser(appUserUpdateRequest, userId);
        return response;
      }
    

    以下是反序列化程序:

    public class MyDateTimeDeserializer extends JsonDeserializer<Date> {
    
      @Override
      public Date deserialize(JsonParser jsonParser, DeserializationContext context)
          throws IOException, JsonProcessingException {
        try {
          return DataTypeHelper.stringToDateTime(jsonParser.getText());
        } catch (MyOwnWrittenException ex) {
          throw ex;
        }
      }  
    }
    

    在一个 DataTypeHelper.stringToDateTime 是一些阻止无效日期字符串的验证。 还有一个处理我的异常的处理程序:

    @ControllerAdvice
    public class MyExceptionHandler extends ResponseEntityExceptionHandler {
    
      @ExceptionHandler({ MyOwnWrittenException .class})
      protected ResponseEntity<Object> handleInvalidRequest(RuntimeException exc, 
        WebRequest request) {
    
        MyOwnWrittenException ex = (MyOwnWrittenException) exc;
        BasicErrorMessage message; = new BasicErrorMessage(ex.getMessage());    
        AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(message);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return handleExceptionInternal(exc, result, headers, HttpStatus.BAD_REQUEST, request);
      }
    }
    

    问题是当 MyDateTimeDeserializer 已经被扔了它不会掉进 MyExceptionHandler 但我不明白为什么?我做错什么了? 在响应中只是一个带有代码400的空响应(

    向上 多亏了@joe doe的回答,问题解决了。这是我更新的处理程序:

    @Order(Ordered.HIGHEST_PRECEDENCE)    
    @ControllerAdvice
    public class MyExceptionHandler extends ResponseEntityExceptionHandler {
    
      @ExceptionHandler({ MyOwnWrittenException .class})
      protected ResponseEntity<Object> handleInvalidRequest(RuntimeException exc, 
        WebRequest request) {
    
        MyOwnWrittenException ex = (MyOwnWrittenException) exc;
        BasicErrorMessage message; = new BasicErrorMessage(ex.getMessage());    
        AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(message);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        return handleExceptionInternal(exc, result, headers, HttpStatus.BAD_REQUEST, request);
      }
    
      @Override
      protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
          HttpHeaders headers, HttpStatus status, WebRequest request) {
        Throwable cause = ex.getCause();
        String message = null;
        if (cause instanceof JsonMappingException) {
          if (cause.getCause() instanceof MyOwnWrittenException) {
            return handleInvalidRequest((RuntimeException) cause.getCause(), request);
          } else {
            message = cause.getMessage();
          }
        } else {
          message = ex.getMessage();
        }
        AbstractUnsuccessfulResult result = new AbstractUnsuccessfulResult(
            new BasicErrorMessage(message));
        headers.setContentType(MediaType.APPLICATION_JSON);
        return handleExceptionInternal(ex, result, headers, HttpStatus.BAD_REQUEST, request);
      }
    }
    

    向上 在我的项目中,没有注释就不能工作 @Order(Ordered.HIGHEST_PRECEDENCE) 我认为这是因为在一个项目中有很多控制器的建议

    1 回复  |  直到 7 年前
        1
  •  1
  •   Joe Doe    7 年前

    之前 updateUser 在调用控制器时,必须解析其参数。这是哪里 HandlerMethodArgumentResolverComposite 进来,委托给一个预先注册的 HandlerMethodArgumentResolver S-在这种特殊情况下,它委托给 RequestResponseBodyMethodProcessor .

    我指的是委托解决者 resolveArgument 方法。此方法间接调用 deserialize 方法,它引发类型为的异常 MyOwnWrittenException .问题是这个异常被包装在另一个异常中。事实上,当它传播回 溶解胶 ,它的类型是 HttpMessageNotReadableException .

    所以,与其抓住 肌肉扭动例外 在自定义异常处理程序中,需要捕获类型为的异常 httpmessagenotreadableexception异常 .然后,在处理这种情况的方法中,您可以检查“原始”异常是否实际上是 肌肉扭动例外 -您可以通过反复调用 getCause 方法。在我的情况下(你的情况可能会一样),我需要打电话给 抽搐 两次“打开”原始异常( httpmessagenotreadableexception异常 -> JsonMappingException -> 肌肉扭动例外 )。

    注意,你不能简单地代替 肌肉扭动例外 具有 httpmessagenotreadableexception异常 在异常处理程序中,因为它(在运行时)与另一个方法冲突,该方法专门设计用于处理后一种类型的异常,称为 handleHttpMessageNotReadable .

    总之,您可以这样做:

    @ControllerAdvice
    public class MyExceptionHandler extends ResponseEntityExceptionHandler {
    
        @Override
        protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
            // ex.getCause().getCause().getClass() gives MyOwnWrittenException
            // the actual logic that handles the exception...
        }
    }