ExceptionMapper 在 JerseyTest 框架中不起作用
ExceptionMapper not working in JerseyTest framework
我为特定对象制作了 RESTFUL-API 并使用 javax.validation.Constraints
进行验证,当我执行应用程序时它按预期工作并且 ExceptionMapper class已正确执行,但当我尝试使用 JerseyFramework 自动化测试时,ExceptionMapper 未执行或处理。所以它在应用程序中有效,但在我测试时无效。
Main Class that launch app
package com.carlos.zerga.server;
public class App {
public static void main( String[] args ) throws Exception {
AppResourceConfig config = new AppResourceConfig();
ServletHolder jerseyServlet
= new ServletHolder(new ServletContainer(config));
Server server = new Server(8081);
ServletContextHandler context
= new ServletContextHandler(server, "/");
context.addServlet(jerseyServlet, "/*");
server.start();
server.join();
}
}
ResourceConfig for App
package com.carlos.zerga.server;
public class AppResourceConfig extends ResourceConfig{
public AppResourceConfig() {
register(new PropertiesBinder());
register(EntityValidationExceptionMapper.class);
packages(true,"com.carlos.zerga.properties");
}
}
ExceptionMapper for ValidationExceptions
package com.carlos.zerga.properties.presentation.handler;
@Provider
public class EntityValidationExceptionMapper implements
ExceptionMapper<BeanValidationException> {
public Response toResponse(BeanValidationException e) {
return Response.status(200).
type(MediaType.APPLICATION_JSON_TYPE).
entity(buildResponse((ConstraintViolationException) e.getInternalException())).
build();
}
private ExceptionResponse buildResponse(ConstraintViolationException e)
{
ArrayList<String> messages = new ArrayList<String>();
for(ConstraintViolation constraintViolation:e.getConstraintViolations())
{
messages.add(constraintViolation.getMessage());
}
return new ExceptionResponse(200, messages,e.getClass().getSimpleName());
}
}
Resouce Class
package com.carlos.zerga.properties.presentation;
@Path("property")
public class PropertiesApiResource {
@POST
@Path("new")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Property createProperty(Property property)
{
return property;
}
//LOT OF OTHER ROUTES
......
}
Test Case
package com.carlos.zerga.test.properties.presentation;
@RunWith(DataProviderRunner.class)
public class PropertiesApiResourceTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig()
.register(new PropertiesBinder())
.register(new EntityValidationExceptionMapper())
.register(new ConstraintValidationException())
.packages(true,"com.carlos.zerga.properties");
}
//Here comes the error
@Test
@UseDataProvider("provideInvalidPropertiesPSR_PA")
public void testInValidPropertyCreation(Property property)
{
Response response=target("property/new").request(MediaType.APPLICATION_JSON).method("POST",Entity.json(property));
assertEquals(response.getMediaType(),MediaType.APPLICATION_JSON_TYPE);
String exceptionResponse=response.readEntity(String.class);
System.out.print(exceptionResponse);
}
POM.xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-metadata-generator</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.tngtech.java</groupId>
<artifactId>junit-dataprovider</artifactId>
<version>1.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-jetty</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jetty-connector</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<jersey.version>2.26-b03</jersey.version>
<jetty.version>9.2.14.v20151106</jetty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
当我通过邮递员发送时 returns 我想要什么:
{"errorCode":200,"errors":["Bathroom : Amount bathrooms can't be empty"],"errorDescription":"ConstraintViolationException"}
但是当我 运行 测试时 returns 我:
javax.ws.rs.ProcessingException:
Exception Description: Constraints violated on marshalled bean:
com.carlos.zerga.properties.domain.entity.Property@3caeaf62
-->Violated constraint on property bathRooms: "Bathroom : Minimun amout of bathrooms is 3".
-->Violated constraint on property bedRooms: "Bathroom : Minimun amout of bathrooms is 3".
Internal Exception: javax.validation.ConstraintViolationException
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:262)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:759)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:756)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:407)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:756)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:443)
at com.carlos.zerga.test.properties.presentation.PropertiesApiResourceTest.testInValidPropertyCreation(PropertiesApiResourceTest.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at com.tngtech.java.junit.dataprovider.DataProviderFrameworkMethod.invokeExplosively(DataProviderFrameworkMethod.java:77)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=17=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: Exception [EclipseLink-7510] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.BeanValidationException
Exception Description: Constraints violated on marshalled bean:
com.carlos.zerga.properties.domain.entity.Property@3caeaf62
-->Violated constraint on property bathRooms: "Bathroom : Minimun amout of bathrooms is 3".
-->Violated constraint on property bedRooms: "Bathroom : Minimun amout of bathrooms is 3".
Internal Exception: javax.validation.ConstraintViolationException
at org.eclipse.persistence.exceptions.BeanValidationException.constraintViolation(BeanValidationException.java:53)
at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:391)
at org.eclipse.persistence.jaxb.JAXBBeanValidator.validate(JAXBBeanValidator.java:275)
at org.eclipse.persistence.jaxb.JAXBMarshaller.validateAndTransformIfNeeded(JAXBMarshaller.java:604)
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:494)
at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.writeTo(MOXyJsonProvider.java:957)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:265)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1126)
at org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:517)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:499)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:393)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:253)
... 34 more
Caused by: javax.validation.ConstraintViolationException
at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:389)
... 47 more
如果仔细查看堆栈跟踪,您会发现此错误实际上发生在客户端,甚至在请求到达服务器之前。原因是您使用的是 MOXy,它使用 JAXB,并且默认启用 JAXB bean 验证。您可以在堆栈跟踪中看到正在调用 JAXBBeanValidator,并且它发生在实体的客户端编组中。
有几个解决方案:
- 配置 MOXy 以禁用 bean 验证
- 不要使用 MOXy
配置 MOXy 以禁用 bean 验证
要配置 MOXy,您可以使用 MoxyJsonConfig
。您实际上也应该在服务器端执行此操作,这样您就不会在服务器上获得双重验证。 Jersey 提供的bean 验证与MOXy 提供的不同。
In Test.java
@Override
public void configureClient(ClientConfig config) {
config.register(new MoxyJsonConfig().setFormattedOutput(true)
.property(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE)
.resolver());
}
configureClient
方法是 JerseyTest
上的一种方法,您可以覆盖它来配置 Client
。您也可以在服务器端使用 ResourceConfig
注册相同的 MoxyJsonConfig
。
不要使用 MOXy
我的 解决方案是去掉 MOXy,改用 Jackson。只需将以下依赖项更改为下面的 Jackson
<!-- remove this -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- use this -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
并且还将 @Valid
标签添加到:
Resource.java
...... MORE ROUTES
@POST
@Path("new")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Property createProperty(@Valid Property property){
return property;
}
...... MORE ROUTES
Jackson 不会有这个问题,因为 Jackson 不做任何 bean 验证。除了这个问题,我个人的看法是 Jackson 对于 JSON 来说只是一个更好的整体库。
注意: 如果您关闭 MOXy 验证(无论是通过配置还是使用 Jackson),您将不再尝试捕获 BeanValidationException
(这是一个 MOXy) class,但你可以直接捕获 ConstraintViolationException
.
我为特定对象制作了 RESTFUL-API 并使用 javax.validation.Constraints
进行验证,当我执行应用程序时它按预期工作并且 ExceptionMapper class已正确执行,但当我尝试使用 JerseyFramework 自动化测试时,ExceptionMapper 未执行或处理。所以它在应用程序中有效,但在我测试时无效。
Main Class that launch app
package com.carlos.zerga.server;
public class App {
public static void main( String[] args ) throws Exception {
AppResourceConfig config = new AppResourceConfig();
ServletHolder jerseyServlet
= new ServletHolder(new ServletContainer(config));
Server server = new Server(8081);
ServletContextHandler context
= new ServletContextHandler(server, "/");
context.addServlet(jerseyServlet, "/*");
server.start();
server.join();
}
}
ResourceConfig for App
package com.carlos.zerga.server;
public class AppResourceConfig extends ResourceConfig{
public AppResourceConfig() {
register(new PropertiesBinder());
register(EntityValidationExceptionMapper.class);
packages(true,"com.carlos.zerga.properties");
}
}
ExceptionMapper for ValidationExceptions
package com.carlos.zerga.properties.presentation.handler;
@Provider
public class EntityValidationExceptionMapper implements
ExceptionMapper<BeanValidationException> {
public Response toResponse(BeanValidationException e) {
return Response.status(200).
type(MediaType.APPLICATION_JSON_TYPE).
entity(buildResponse((ConstraintViolationException) e.getInternalException())).
build();
}
private ExceptionResponse buildResponse(ConstraintViolationException e)
{
ArrayList<String> messages = new ArrayList<String>();
for(ConstraintViolation constraintViolation:e.getConstraintViolations())
{
messages.add(constraintViolation.getMessage());
}
return new ExceptionResponse(200, messages,e.getClass().getSimpleName());
}
}
Resouce Class
package com.carlos.zerga.properties.presentation;
@Path("property")
public class PropertiesApiResource {
@POST
@Path("new")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Property createProperty(Property property)
{
return property;
}
//LOT OF OTHER ROUTES
......
}
Test Case
package com.carlos.zerga.test.properties.presentation;
@RunWith(DataProviderRunner.class)
public class PropertiesApiResourceTest extends JerseyTest {
@Override
protected Application configure() {
return new ResourceConfig()
.register(new PropertiesBinder())
.register(new EntityValidationExceptionMapper())
.register(new ConstraintValidationException())
.packages(true,"com.carlos.zerga.properties");
}
//Here comes the error
@Test
@UseDataProvider("provideInvalidPropertiesPSR_PA")
public void testInValidPropertyCreation(Property property)
{
Response response=target("property/new").request(MediaType.APPLICATION_JSON).method("POST",Entity.json(property));
assertEquals(response.getMediaType(),MediaType.APPLICATION_JSON_TYPE);
String exceptionResponse=response.readEntity(String.class);
System.out.print(exceptionResponse);
}
POM.xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>hk2-metadata-generator</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.tngtech.java</groupId>
<artifactId>junit-dataprovider</artifactId>
<version>1.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-jetty</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.connectors</groupId>
<artifactId>jersey-jetty-connector</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<jersey.version>2.26-b03</jersey.version>
<jetty.version>9.2.14.v20151106</jetty.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
当我通过邮递员发送时 returns 我想要什么:
{"errorCode":200,"errors":["Bathroom : Amount bathrooms can't be empty"],"errorDescription":"ConstraintViolationException"}
但是当我 运行 测试时 returns 我:
javax.ws.rs.ProcessingException:
Exception Description: Constraints violated on marshalled bean:
com.carlos.zerga.properties.domain.entity.Property@3caeaf62
-->Violated constraint on property bathRooms: "Bathroom : Minimun amout of bathrooms is 3".
-->Violated constraint on property bedRooms: "Bathroom : Minimun amout of bathrooms is 3".
Internal Exception: javax.validation.ConstraintViolationException
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:262)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:759)
at org.glassfish.jersey.client.JerseyInvocation.call(JerseyInvocation.java:756)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:407)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:756)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:443)
at com.carlos.zerga.test.properties.presentation.PropertiesApiResourceTest.testInValidPropertyCreation(PropertiesApiResourceTest.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at com.tngtech.java.junit.dataprovider.DataProviderFrameworkMethod.invokeExplosively(DataProviderFrameworkMethod.java:77)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=17=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: Exception [EclipseLink-7510] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.BeanValidationException
Exception Description: Constraints violated on marshalled bean:
com.carlos.zerga.properties.domain.entity.Property@3caeaf62
-->Violated constraint on property bathRooms: "Bathroom : Minimun amout of bathrooms is 3".
-->Violated constraint on property bedRooms: "Bathroom : Minimun amout of bathrooms is 3".
Internal Exception: javax.validation.ConstraintViolationException
at org.eclipse.persistence.exceptions.BeanValidationException.constraintViolation(BeanValidationException.java:53)
at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:391)
at org.eclipse.persistence.jaxb.JAXBBeanValidator.validate(JAXBBeanValidator.java:275)
at org.eclipse.persistence.jaxb.JAXBMarshaller.validateAndTransformIfNeeded(JAXBMarshaller.java:604)
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:494)
at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.writeTo(MOXyJsonProvider.java:957)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:265)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250)
at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1126)
at org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:517)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:499)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:393)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:253)
... 34 more
Caused by: javax.validation.ConstraintViolationException
at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:389)
... 47 more
如果仔细查看堆栈跟踪,您会发现此错误实际上发生在客户端,甚至在请求到达服务器之前。原因是您使用的是 MOXy,它使用 JAXB,并且默认启用 JAXB bean 验证。您可以在堆栈跟踪中看到正在调用 JAXBBeanValidator,并且它发生在实体的客户端编组中。
有几个解决方案:
- 配置 MOXy 以禁用 bean 验证
- 不要使用 MOXy
配置 MOXy 以禁用 bean 验证
要配置 MOXy,您可以使用 MoxyJsonConfig
。您实际上也应该在服务器端执行此操作,这样您就不会在服务器上获得双重验证。 Jersey 提供的bean 验证与MOXy 提供的不同。
In Test.java
@Override
public void configureClient(ClientConfig config) {
config.register(new MoxyJsonConfig().setFormattedOutput(true)
.property(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE)
.resolver());
}
configureClient
方法是 JerseyTest
上的一种方法,您可以覆盖它来配置 Client
。您也可以在服务器端使用 ResourceConfig
注册相同的 MoxyJsonConfig
。
不要使用 MOXy
我的 解决方案是去掉 MOXy,改用 Jackson。只需将以下依赖项更改为下面的 Jackson
<!-- remove this -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- use this -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
</dependency>
并且还将 @Valid
标签添加到:
Resource.java
...... MORE ROUTES
@POST
@Path("new")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Property createProperty(@Valid Property property){
return property;
}
...... MORE ROUTES
Jackson 不会有这个问题,因为 Jackson 不做任何 bean 验证。除了这个问题,我个人的看法是 Jackson 对于 JSON 来说只是一个更好的整体库。
注意: 如果您关闭 MOXy 验证(无论是通过配置还是使用 Jackson),您将不再尝试捕获 BeanValidationException
(这是一个 MOXy) class,但你可以直接捕获 ConstraintViolationException
.