Java Glassfish 4.1中Object.afterUnmarshal的捕获异常
我目前正在JDK1.8.0-40上运行Glassfish 4.1。我使用的是javaee-web-api-7.0和jersey-media-moxy-2.22。我正在将JSON和XML从JAXB注释的java对象编组/解编组到JAXB注释的java对象 我设置了一个Java Glassfish 4.1中Object.afterUnmarshal的捕获异常,java,jaxb,glassfish,glassfish-4.1,Java,Jaxb,Glassfish,Glassfish 4.1,我目前正在JDK1.8.0-40上运行Glassfish 4.1。我使用的是javaee-web-api-7.0和jersey-media-moxy-2.22。我正在将JSON和XML从JAXB注释的java对象编组/解编组到JAXB注释的java对象 我设置了一个ContextResolver来提供一个带有自定义ValidationEventHandler的Unmarshaller,它将从属性设置器收集异常,并抛出一个带有聚合验证错误的BadRequestException。这部分工作正常 但
ContextResolver
来提供一个带有自定义ValidationEventHandler
的Unmarshaller
,它将从属性设置器收集异常,并抛出一个带有聚合验证错误的BadRequestException
。这部分工作正常
但是,对于检查未设置属性的对象,我还有beforemashal
和afterUnmarshal
方法(必须设置属性的规则因属性值而异,这导致我排除了对架构的验证)。如果从afterUnmarshal
方法引发异常,则ValidationEventHandler
不会看到该异常,而是冒泡到exceptionapper
是否有任何方法可以捕获单个对象上的beforemashal
和afterUnmarshal
方法中的异常,并将它们发送到ValidationEventHandler
我认为可以实现一个MessageBodyReader
来捕获异常,使用Unmarshaller.getEventHandler
,手动调用ValidationEventHandler.handleEvent
,如果handleEvent
返回false,则抛出一个BadRequestException
[编辑:如果从解组器.unmarshal中引发异常,则无法继续解组,因此唯一可能的方法是终止处理]。但这会丢失事件位置信息,我并不特别喜欢实现我自己的MessageBodyReader
。我希望有一种更简单的内置方法来实现这一点,但我还没有发现
提前感谢您的帮助。经过一番挖掘和头痛之后,我终于找到了一个解决方案
步骤1(可选)
编辑:您不必通过修补Jersey来实现此行为。我在任何地方都找不到它的文档,但是org.glassfish.Jersey.internal.inject.Custom
注释将提供者标记为Custom(而仅@provider
是不够的)。您还必须通过将CommonProperties.MOXy\u JSON\u FEATURE\u disable
设置为true来禁用MOXy,如果您的提供程序用于application/JSON
。因此,您只需执行以下操作:
@Custom
@Provider
public class MyCustomMessageBodyReader...
[...]
这是我解决方案中最不喜欢的部分,但也避免了大量代码重复。Jersey用于选择MessageBodyReader/Writer
的排序算法无法对应用程序提供商进行优先级排序(我可以找到)。我想扩展AbstractRootElementJaxbProvider
以重用其功能,但这意味着我无法使其比提供的Jersey更具体XmlRootElementJaxbProvider
。因为默认情况下Jersey仅根据媒体类型距离、对象类型距离以及提供者是否注册为自定义提供者进行排序(通过@Provider
注释检测到的提供程序未注册为自定义提供程序),将始终选择Jersey实现,而不是myMessageBodyReader/Writer
我从Github签出了Jersey 2.10.4源代码,并修补了MessageBodyFactory
,以利用@Priority
注释作为MessageBodyReader/Writer
选择算法的一部分。
步骤3
最后,我扩展了org.glassfish.jersey.message.internal.AbstractRootElementJaxbProvider
以覆盖readFrom
方法
@Override
protected Object readFrom(
Class<Object> type,
MediaType mediaType,
Unmarshaller u,
InputStream entityStream)
throws JAXBException
{
final SAXSource source = getSAXSource(spf.provide(), entityStream);
ValidationEventCollector eventCollector = new ValidationEventCollector();
ValidatingUnmarshallerListener listener = new ValidatingUnmarshallerListener(eventCollector);
u.setEventHandler(eventCollector);
u.setListener(listener);
final Object result;
if (type.isAnnotationPresent(XmlRootElement.class))
{
result = u.unmarshal(source);
}
else
{
result = u.unmarshal(source, type).getValue();
}
if (eventCollector.hasEvents())
{
HttpError error = new HttpError(Response.Status.BAD_REQUEST);
for (ValidationEvent event : eventCollector.getEvents())
{
error.addMessage(ValidationUtil.toString(event));
}
throw new WebApplicationException(error.toResponse());
}
return result;
}
@覆盖
从中读取受保护对象(
类类型,
MediaType MediaType,
破译,
输入流(实体流)
抛出异常
{
最终SAXSource=getSAXSource(spf.provide(),entityStream);
ValidationEventCollector eventCollector=新的ValidationEventCollector();
ValidatingUnmarshallerListener侦听器=新的ValidatingUnmarshallerListener(eventCollector);
u、 setEventHandler(eventCollector);
u、 setListener(listener);
最终目标结果;
if(type.isAnnotationPresent(XmlRootElement.class))
{
结果=u.unmarshal(来源);
}
其他的
{
结果=u.unmarshal(源,类型).getValue();
}
if(eventCollector.hasEvents())
{
HttpError error=新的HttpError(Response.Status.BAD_请求);
对于(ValidationEvent事件:eventCollector.getEvents())
{
error.addMessage(ValidationUtil.toString(事件));
}
抛出新的WebApplicationException(error.toResponse());
}
返回结果;
}
经过一番挖掘和头痛之后,我终于找到了一个解决方案
步骤1(可选)
编辑:您不必通过修补Jersey来实现此行为。我在任何地方都找不到它的文档,但是org.glassfish.Jersey.internal.inject.Custom
注释将提供者标记为Custom(而仅@provider
是不够的)。您还必须通过将CommonProperties.MOXy\u JSON\u FEATURE\u disable
设置为true来禁用MOXy,如果您的提供程序用于application/JSON
。因此,您只需执行以下操作:
@Custom
@Provider
public class MyCustomMessageBodyReader...
[...]
这是我解决方案中最不喜欢的部分,但也避免了大量代码重复。Jersey用于选择MessageBodyReader/Writer
的排序算法无法对应用程序提供商进行优先级排序(我可以找到)。我想扩展AbstractRootElementJaxbProvider
以重用其功能,但这意味着我无法使其比提供的Jersey更具体XmlRootElementJaxbProvider
。因为默认情况下Jersey仅根据媒体类型距离、对象类型距离以及提供者是否注册为自定义提供者进行排序(提供
@Override
protected Object readFrom(
Class<Object> type,
MediaType mediaType,
Unmarshaller u,
InputStream entityStream)
throws JAXBException
{
final SAXSource source = getSAXSource(spf.provide(), entityStream);
ValidationEventCollector eventCollector = new ValidationEventCollector();
ValidatingUnmarshallerListener listener = new ValidatingUnmarshallerListener(eventCollector);
u.setEventHandler(eventCollector);
u.setListener(listener);
final Object result;
if (type.isAnnotationPresent(XmlRootElement.class))
{
result = u.unmarshal(source);
}
else
{
result = u.unmarshal(source, type).getValue();
}
if (eventCollector.hasEvents())
{
HttpError error = new HttpError(Response.Status.BAD_REQUEST);
for (ValidationEvent event : eventCollector.getEvents())
{
error.addMessage(ValidationUtil.toString(event));
}
throw new WebApplicationException(error.toResponse());
}
return result;
}