ExceptionMapper在JerseyTest框架中不起作用

时间:2017-06-25 15:07:20

标签: java maven jetty jersey-2.0 jersey-test-framework

我为特定对象创建了一个RESTFUL-API并使用javax.validation.Constraints进行验证,当我执行应用程序时,它按预期工作,并且ExceptionMapper类已正确执行但当我尝试使用JerseyFramework自动化测试时不执行或处理ExceptionMapper。所以它适用于应用程序,但不是我测试时。

  

启动应用的主要类

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();
   } 
}
  

App的ResourceConfig

package com.carlos.zerga.server;
public class AppResourceConfig extends ResourceConfig{

    public AppResourceConfig() {
        register(new PropertiesBinder());
        register(EntityValidationExceptionMapper.class);
       packages(true,"com.carlos.zerga.properties");
    }
}
  

ValidationExceptions的ExceptionMapper

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());
    }

}
  

资源等级

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
    ......
}
  

测试用例

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>

当我通过邮递员发送时,它会返回我想要的内容:

{"errorCode":200,"errors":["Bathroom : Amount bathrooms can't be empty"],"errorDescription":"ConstraintViolationException"}

但是当我运行测试时,请回复我:

    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$1.call(JerseyInvocation.java:759)
    at org.glassfish.jersey.client.JerseyInvocation$1.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$1.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$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.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

1 个答案:

答案 0 :(得分:2)

如果仔细查看堆栈跟踪,即使在请求到达服务器之前,您仍会看到此错误实际发生在客户端。原因是您正在使用MOXy,它使用JAXB,默认情况下启用JAXB bean验证。您可以在stacktrace中看到正在调用JAXBBeanValidator,并且它发生在客户端编组实体上。

有几种解决方案:

  • 配置MOXy以禁用bean验证
  • 不要使用MOXy

配置MOXy以禁用bean验证

要配置MOXy,您可以使用MoxyJsonConfig。实际上,您应该在服务器端执行此操作,这样您就不会在服务器上进行双重验证。 Jersey提供的bean验证与MOXy提供的验证不同。

  

在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的方法。您也可以为服务器端注册与MoxyJsonConfig相同的ResourceConfig

不要使用MOXy

我的解决方案就是摆脱MOXy,然后使用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

杰克逊没有做任何豆类验证,你不会遇到这个问题。除了这个问题,我个人认为杰克逊无论如何都只是一个更好的JSON整体库。

注意:如果您关闭MOXy验证(无论是通过配置还是使用Jackson),您将不再尝试捕获BeanValidationException(这是一个MOXy)类,但你可以直接捕捉ConstraintViolationException