[英]Throw custom Exception with HTTP status code for Jackson Custom deserializer
我有這個 InstantDesrializer
@Slf4j
public class InstantDeserializer extends StdDeserializer<Instant> {
public InstantDeserializer() {
this(null);
}
public InstantDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Instant deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
log.info(node.asText());
TemporalAccessor parse = null;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT).withZone(ZoneOffset.UTC);
try {
parse = dateTimeFormatter.parse(node.asText());
} catch (Exception e) {
e.printStackTrace();
throw new IOException();
}
log.info(Instant.from(parse).toString());
return Instant.from(parse);
}
}
然后在@ControllerAdvice
中對應IOException
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException e) {
return ResponseEntity.status(422).build();
}
這在我的 DTO 中:
@NotNull
@JsonDeserialize(using = InstantDeserializer.class)
// @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private Instant timestamp;
即使取消注釋@DateTimeFormat
,它也不起作用
理想情況下,它應該返回 422 狀態。 但是,它返回 400。
也許我只是錯過了一些我無法弄清楚的小東西。
此處建議使用此方法: Throw custom exception while deserializing the Date field using jackson in java
永遠不會調用Controller方法,因為JSON正文解析引發了異常。
由於未調用Controller方法,因此未應用@ContollerAdvice。
您的handleIOException方法不會被調用,並且您的422狀態不會被應用。
我懷疑這是更詳細的情況...
HTTP請求包含json正文。
與@RequestMapping和請求的其他注釋匹配的控制器方法將DTO類的實例作為參數。
Spring會在調用控制方法之前嘗試反序列化傳入的json主體。 為了傳遞DTO對象,它必須這樣做。
反序列化使用您的自定義反序列化器,它引發IOException。
此IOException發生在調用控制器方法之前 。 實際上,永遠不會為該請求調用控制器方法。
Spring使用默認行為處理異常,返回HTTP400。Spring具有非常廣泛的RFC 7231 HTTP 400概念。
由於永遠不會調用控制器方法,因此不會應用@ControllerAdvice,並且@ExceptionHandler不會看到異常。 狀態未設置為422。
為什么我相信這一點?
我經常從Spring看到這種行為,並且我認為這是預期的行為。 但是我沒有找到文檔或閱讀源代碼來確定。
你能為這個做什么?
您可能不喜歡的一種簡單方法是,聲明您的控制器方法以接受幾乎永不失敗的輸入,例如String。
您負責驗證和反序列化輸入,並確定要返回的狀態和消息。
您致電Jackson進行反序列化。 使用您的@ExceptionHandler方法。
獎勵:您可以返回傑克遜經常有用的解析錯誤消息的文本。 這些可以幫助客戶弄清楚為什么他們的json被拒絕。
如果Spring提供一種更時尚的方法,一個要子類化的類,一個特殊的注釋,我不會感到驚訝。 我沒有追求。
你應該怎么做?
我寧願不提起400 vs. 422訴訟。 根據您的優先級,最好接受Spring的約定。
RFC 7231處於狀態400
400(錯誤請求)狀態代碼表示服務器由於某些原因(例如格式錯誤的請求語法,無效的請求消息框架或欺騙性的請求路由)而被視為客戶端錯誤,因此服務器無法處理該請求。
如果HTTP狀態代碼警察選擇了您,則可以指向它並說“ 我認為此輸入是客戶端錯誤。 ”然后辯稱422是不合適的,除非您正在提供WebDAV只是為了使它們保持平衡。
您不需要handleIOException
方法,只需添加@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
到您的CustomException。
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public class MyException extends JsonProcessingException {
public MyException(String message) {
super(message);
}
}
因此,當您對正文提出無效要求時
{"timestamp":"2018-04-2311:32:22","id":"132"}
響應將是:
{
"timestamp": 1552990867074,
"status": 422,
"error": "Unprocessable Entity",
"message": "JSON parse error: Instant field deserialization failed; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Instant field deserialization failed (through reference chain: TestDto[\"timestamp\"])"
}
使用有效的請求可以正常工作:
{"timestamp":"2018-04-23T11:32:22.213Z","id":"132"}
響應:
{
"id": "132",
"timestamp": {
"nano": 213000000,
"epochSecond": 1514700142
}
}
Jackson在反序列化錯誤的情況下拋出的異常是HttpMessageNotReadableException。
在您的自定義反序列化器中,您可以拋出擴展 JsonProcessingException 的自己的反序列化異常。
在您的 ControllerAdvice 中,您可以處理 HttpMessageNotReadableException 並獲取其原因,這是您的自定義異常。 這樣,你就可以拋出你想要的http代碼了。
@ExceptionHandler({HttpMessageNotReadableException.class})
@ResponseBody
public ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
Throwable cause = ex.getCause();
if (cause.getCause() instanceof YourOwnException) {
//Return your response entity with your custom HTTP code
}
//Default exception handling
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.