我有这个InstantDesrializer
<bean id="customerRequestJsonProvider"class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
<property name="writeXsiType" value="false" />
<property name="serializeAsArray" value="true" />
<property name="writeNullAsString" value="true" />
</bean>
然后在@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
这在我的DTO中:
@ExceptionHandler(IOException.class)
public ResponseEntity<String> handleIOException(IOException e) {
return ResponseEntity.status(422).build();
}
即使未注释 @NotNull
@JsonDeserialize(using = InstantDeserializer.class)
// @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
private Instant timestamp;
,它也不起作用
理想情况下,它应该返回422状态。但是,它返回400。
也许我只是缺少了一些小东西,我无法弄清楚。
在此建议此方法: Throw custom exception while deserializing the Date field using jackson in java
答案 0 :(得分:0)
从不调用您的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只是为了让他们保持平衡。
答案 1 :(得分:0)
您不需要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
}
}