簡體   English   中英

在 ControllerAdvice 之前捕獲反序列化異常

[英]Catch a deserialization exception before a ControllerAdvice

這是一個問題:我有一個采用輸入模型的控制器。 可以說

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(

UPD感謝@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);
  }
}

UPD在我的項目中,如果沒有注釋@Order(Ordered.HIGHEST_PRECEDENCE)我相信這是因為項目中的 ControllerAdvices 數量

在調用控制器中的updateUser之前,必須解析其參數。 這是HandlerMethodArgumentResolverComposite用武之地,並委托給預注冊的HandlerMethodArgumentResolver - 在這種特殊情況下,它委托給RequestResponseBodyMethodProcessor

通過委托,我的意思是調用解析器的resolveArgument方法。 這種方法間接地調用deserialize從解串器,其拋出類型的異常方法MyOwnWrittenException 問題是這個異常被另一個異常包裹了。 事實上,當它傳播回resolveArgument ,它的類型是HttpMessageNotReadableException

因此, MyOwnWrittenException在自定義異常處理程序中捕獲MyOwnWrittenException ,不如捕獲MyOwnWrittenException類型的HttpMessageNotReadableException 然后,在處理這種情況的方法中,您可以檢查“原始”異常是否實際上是MyOwnWrittenException - 您可以通過重復調用getCause方法來做到這一點。 在我的情況下(它可能與您的情況相同),我需要調用getCause兩次以“解開”原始異常( HttpMessageNotReadableException -> JsonMappingException -> MyOwnWrittenException )。

請注意,您不能在異常處理程序中簡單地將MyOwnWrittenException替換為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...
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM