如何在javax.json.bind.JsonbException(Helidon项目)上返回自定义响应



当json有效负载无效时,我试图返回自定义http响应(400错误请求(,而不是http 500服务器错误
问题是,当我在ExceptionMapper<T>中使用javax.json.bind.JsonbException作为异常类型时,它无法捕获它,但我可以捕获更通用的javax.ws.rs.ProcessingException
很明显,javax.ws.rs.ProcessingException是由javax.json.bind.JsonbException引起的,因为堆栈跟踪显示:

javax.ws.rs.ProcessingException: Error deserializing object from entity stream.
at org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:86)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:233)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:212)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:49)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:290)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:73)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:56)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:50)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:68)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:109)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$VoidOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:159)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:255)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:684)
at io.helidon.webserver.jersey.JerseySupport$JerseyHandler.lambda$doAccept$4(JerseySupport.java:326)
at io.helidon.common.context.Contexts.runInContext(Contexts.java:117)
at io.helidon.common.context.ContextAwareExecutorImpl.lambda$wrap$7(ContextAwareExecutorImpl.java:154)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: javax.json.bind.JsonbException: Unable to deserialize property 'date' because of: Error parsing class java.util.Date from value: 2022-03-31T10:37:23.005Z[UTC]x. Check your @JsonbDateFormat has all time units for class java.util.Date type, or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:100)
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:64)
at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:62)
at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:51)
at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:59)
at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:99)
at org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:84)
... 33 more
Caused by: javax.json.bind.JsonbException: Error parsing class java.util.Date from value: 2022-03-31T10:37:23.005Z[UTC]x. Check your @JsonbDateFormat has all time units for class java.util.Date type, or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.
at org.eclipse.yasson.internal.serializer.AbstractDateTimeDeserializer.deserialize(AbstractDateTimeDeserializer.java:74)
at org.eclipse.yasson.internal.serializer.AbstractValueTypeDeserializer.deserialize(AbstractValueTypeDeserializer.java:64)
at org.eclipse.yasson.internal.serializer.ObjectDeserializer.deserializeNext(ObjectDeserializer.java:180)
at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:94)
... 39 more
Caused by: java.time.format.DateTimeParseException: Text '2022-03-31T10:37:23.005Z[UTC]x' could not be parsed, unparsed text found at index 29
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2055)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954)
at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:600)
at org.eclipse.yasson.internal.serializer.DateTypeDeserializer.parseWithOrWithoutZone(DateTypeDeserializer.java:79)
at org.eclipse.yasson.internal.serializer.DateTypeDeserializer.parseDefault(DateTypeDeserializer.java:49)
at org.eclipse.yasson.internal.serializer.DateTypeDeserializer.parseDefault(DateTypeDeserializer.java:29)
at org.eclipse.yasson.internal.serializer.AbstractDateTimeDeserializer.deserialize(AbstractDateTimeDeserializer.java:72)
... 42 more

但我想知道是否有办法赶上javax.json.bind.JsonbException

我使用的是Helidon MP 2.4.2。

再现:

  1. 创建Helidon MP链接的快速启动
  2. 在问候语资源上添加post方法
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void add(MyClass cal){
System.out.println(cal.getDate());
}
public class MyClass {
private String name;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public MyClass(String name, Date date) {
this.name = name;
this.date = date;
}
public MyClass() {
}

}

3.发送一个json日期无效的post请求。有效支付:

{
"name": "testg",
"date": "2022-03-31T10:37:23.005Z[UTC]"
}

无效(日期(有效载荷:

{
"name": "testg",
"date": "2022-03-31T10:37:23.005Z[UTC]XXXXXXXX"
}

我也遇到过类似的问题,我得到了这个解决方案,遵循了之前的解决方案(Jersey:当给出无效的请求体时,返回400错误,而不是500错误(。

import java.time.format.DateTimeParseException;
import javax.json.bind.JsonbException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.apache.johnzon.mapper.MapperException;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
@Provider
public class InvalidDataMapper implements ExceptionMapper<JsonbException>         {
@Override
public Response toResponse(JsonbException arg0) {
if (arg0.getCause() instanceof MapperException) {
MapperException exception = (MapperException) arg0.getCause();
ErrorMessage errorMessage = null;
if (exception.getCause() instanceof DateTimeParseException) {
errorMessage = new ErrorMessage(CustomCodesAndMessages.CODE_ERROR_FORMAT_DATE, exception.getMessage());
}
if (exception.getCause() instanceof NumberFormatException) {
errorMessage = new ErrorMessage(CustomCodesAndMessages.CODE_ERROR_FORMATO_NUMERIC, exception.getMessage());
}
return Response.status(Response.Status.BAD_REQUEST).type(APPLICATION_JSON_TYPE).entity(errorMessage).build();
}
return Response.status(Response.Status.BAD_REQUEST).build();
}
}

ErrorMessage是

package fif.tech.utiles;
import java.util.Objects;
import org.apache.cxf.jaxrs.ext.Nullable;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorMessage {
private final int code;
private final String message;
private final String details;
public ErrorMessage(String message) {
this(500, message);
}
public ErrorMessage(int code, String message) {
this(code, message, null);
}
@JsonCreator
public ErrorMessage(@JsonProperty("code") int code,     @JsonProperty("message") String message,
@Nullable @JsonProperty("details") String details) {
this.code = code;
this.message = message;
this.details = details;
}
@JsonProperty("code")
public Integer getCode() {
return code;
}
@JsonProperty("message")
public String getMessage() {
return message;
}
@JsonProperty("details")
@Nullable
public String getDetails() {
return details;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (getClass() != obj.getClass())) {
return false;
}
final ErrorMessage other = (ErrorMessage) obj;
return Objects.equals(code, other.code)
&& Objects.equals(message, other.message)
&& Objects.equals(details, other.details);
}
@Override
public int hashCode() {
return Objects.hash(code, message, details);
}
@Override
public String toString() {
return "ErrorMessage{code=" + code + ", message='" + message + "', details='" + details + "'}";
}
}

现在我正试图获取产生异常的详细字段,但我认为这已经是一个足够的解决方案了。

最新更新