2017-06-25 39 views
2

我爲特定對象製作了RESTFUL-API,並使用javax.validation.Constraints進行了驗證,當我執行應用程序時,它按預期方式工作,並且ExceptionMapper類已正確執行,但當我嘗試使用自動化測試JerseyFramework不執行或處理ExceptionMapper。所以它適用於應用程序,但不是當我測試它時。ExceptionMapper在JerseyTest框架中不工作

主類,啓動應用

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

} 

種源類

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: 
    [email protected] 
    -->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: 
    [email protected] 
    -->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

如果在堆棧跟蹤仔細看,你會看到這個錯誤實際上發生在客戶端,甚至在請求到達服務器之前。原因是您使用的是使用JAXB的MOXy,並且默認情況下啓用了JAXB bean驗證。您可以在堆棧跟蹤中看到JAXBBeanValidator正在被調用,並且它發生在實體的客戶端編組上。

有幾個解決方案:

  • 配置莫西禁用Bean驗證
  • 不要使用莫西

配置莫西禁用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的方法。您也可以在服務器端註冊同樣的MoxyJsonConfigResourceConfig

不要使用莫西

的解決辦法是剛剛擺脫莫西,並用傑克遜來代替。只要改變以下依賴於傑克遜在它下面

<!-- 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 

你不會傑克遜有這個問題,因爲傑克遜沒有做任何bean驗證。除了這個問題,我個人的觀點是傑克遜無論如何都只是一個更好的JSON整體庫。

注:如果您關閉莫西驗證(無論是通過配置或通過使用傑克遜),你將不再追趕一輛BeanValidationException(這是一個莫西)類,而是可以直接抓到ConstraintViolationException