[英]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.