JTF的Unable to invoke request异常或Unable to find a MessageBodyReader of content-type application..异常详解 逃离我推掉我的手 2022-05-14 14:12 118阅读 0赞 基于Jersey开发的一个操作OpenStack的REST服务,利用Jersey的Test Framework编写单元测试类如下: public class RestAddressTest extends JerseyTest { Integer autoId = 1; @BeforeClass public void before() throws Exception { super.setUp(); } @AfterClass public void after() throws Exception { super.tearDown(); } @Override protected Application configure() { return new ResourceConfig(RestAddress.class); } @Test(priority = 0) public void testAdd() { Address ac = new Address(); ac.setGateway("1.1.1.1"); ac.setName("CLOUD_TEST_BJ"); ac.setNicName("ipv4"); Response res = target("address").request(MediaType.APPLICATION_JSON).post(Entity.entity(ac, MediaType.APPLICATION_JSON), Response.class); Assert.assertEquals(200, res.getStatus()); } @Test(priority = 1) public void testGet() { Response res = target("address").request(MediaType.APPLICATION_JSON).get(); List<Address> acl = res.readEntity(new GenericType<List<Address>>() {}); this.autoId = acl.get(0).getAutoId(); Assert.assertEquals(1, acl.size()); } @Test(priority = 2) public void testDelete() { Response response = target("address").path(String.valueOf(autoId)).request(MediaType.APPLICATION_JSON).delete(); Assert.assertEquals(200, response.getStatus()); } } 但是在测试时,却始终遇到如下异常: ... java.lang.IllegalArgumentException: attempt to create delete event with null entity at org.hibernate.event.spi.DeleteEvent.<init>(DeleteEvent.java:31) at org.hibernate.internal.SessionImpl.delete(SessionImpl.java:911) ... [03:39:54,116 ERROR] rollbackTxn - Transaction.rollback [03:39:54,143 INFO ] Stopped ServerConnector@54089484{HTTP/1.1}{0.0.0.0:9998} [03:39:54,146 WARN ] /address/1 java.lang.RuntimeException: java.lang.IllegalArgumentException: Status code of the supplied response [500] is not from the required status code family "CLIENT_ERROR". at org.glassfish.jersey.jetty.JettyHttpContainer.handle(JettyHttpContainer.java:197) ... [03:39:54,147 WARN ] Could not send response error 500: java.lang.RuntimeException: java.lang.IllegalArgumentException: Status code of the supplied response [500] is not from the required status code family "CLIENT_ERROR". Oct 24, 2018 5:39:54 AM org.glassfish.jersey.test.jetty.JettyTestContainerFactory$JettyTestContainer <init> INFO: Creating JettyTestContainer configured at the base URI http://localhost:9998/ [03:39:54,275 INFO ] jetty-9.2.14.v20151106 [03:39:54,279 INFO ] Started ServerConnector@5042e3d0{HTTP/1.1}{0.0.0.0:9998} [03:39:54,280 INFO ] Started @5991ms [03:39:54,312 INFO ] Stopped ServerConnector@5042e3d0{HTTP/1.1}{0.0.0.0:9998} ... Tests run: 4, Failures: 3, Errors: 0, Skipped: 0, Time elapsed: 4.734 sec <<< FAILURE! Results : Failed tests: testAdd(com.mycompany.myapp.rest.RestAddressTest): RESTEASY004655: Unable to invoke request testGet(com.mycompany.myapp.rest.RestAddressTest): RESTEASY003145: Unable to find a MessageBodyReader of content-type application/json and type interface java.util.List testDelete(com.mycompany.myapp.rest.RestAddressTest): expected [500] but found [200] ... 分析异常,可以发现在执行测试用例testDelete()时异常,这里的信息有很强的迷惑性。事实上,在测试用例testDelete()之前执行的testAdd()和testGet()都已经发生了异常,但是却没有输出错误信息。 继续分析发现这应该是个“CLIENT\_ERROR”。 在最后的测试结果中,终于暴露了错误的根源。原来,发生错误的是**RESTEASY**,等等,我们利用Jersey的测试框架进行单元测试,哪里来的RestEasy呢? 检查项目依赖,发现果然同时存在**Jersey-Client**和**RestEasy-Client**。突然想到,存在OpenStack的OpenStack4j就是依赖了RestEasy。查看OpenStack4j 3.1.0果然是其引入了RestEasy。 那么RestEasy为什么会在这里被调用呢?感谢StackOverflow,我很快找到了答案。 原来,同样作为JAX-RS的实现,Jersey和RestEasy都扩展了**javax.ws.rs.client.ClientBuilder**类,并实现了**javax.ws.rs.client.Client**接口。 其中,Jersey提供的是org.glassfish.jersey.client.JerseyClientBuilder和org.glassfish.jersey.client.JerseyClient, 而RestEasy提供的是org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder和org.jboss.resteasy.client.jaxrs.ResteasyClient。 在同时存在两个JAX-RS实现的时候,由于JAX-RS采用了Java SPI的服务实现注入机制。RestEasy响应了这种机制,声明了ResteasyClientBuilder实现如下: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhaXlhbmdkYW8_size_27_color_FFFFFF_t_70][] 而Jersey对此机制却无动于衷,实现中根本没有给出JerseyClientBuilder的实现,如下所示: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhaXlhbmdkYW8_size_27_color_FFFFFF_t_70 1][] 所以,ResteasyClientBuilder的优先级高于JerseyClientBuilder而被采用。这样,在测试过程中,客户端事实上使用的是RestEasy的**ResteasyClient**,而非Jersey测试框架期望的JerseyClient。 找到问题的根源,解决起来也很容易了,只要在测试类中重写JerseyTest的getClient()方法,明确指定JerseyClient即可,修改测试类如下: @Override public Client getClient() { return JerseyClientBuilder.createClient(); } 参考链接: [https://jersey.github.io/documentation/latest/test-framework.html][https_jersey.github.io_documentation_latest_test-framework.html] [https://github.com/jersey/jersey/tree/master/test-framework][https_github.com_jersey_jersey_tree_master_test-framework] [https://stackoverflow.com/questions/36348675/unable-to-test-jax-rs-with-json-entity][https_stackoverflow.com_questions_36348675_unable-to-test-jax-rs-with-json-entity] [https://stackoverflow.com/questions/48337023/eclipse-jerseytest-getclient-returns-resteasyclient][https_stackoverflow.com_questions_48337023_eclipse-jerseytest-getclient-returns-resteasyclient] [https://github.com/ContainX/openstack4j/blob/3.1.0/connectors/resteasy/pom.xml][https_github.com_ContainX_openstack4j_blob_3.1.0_connectors_resteasy_pom.xml] [https://github.com/resteasy/Resteasy/blob/master/resteasy-client/src/main/resources/META-INF/services/javax.ws.rs.client.ClientBuilder][https_github.com_resteasy_Resteasy_blob_master_resteasy-client_src_main_resources_META-INF_services_javax.ws.rs.client.ClientBuilder] [https://github.com/jersey/jersey/tree/master/core-client/src/main][https_github.com_jersey_jersey_tree_master_core-client_src_main] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhaXlhbmdkYW8_size_27_color_FFFFFF_t_70]: /images/20220504/1d4da542a6654c298a8018568524c275.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhaXlhbmdkYW8_size_27_color_FFFFFF_t_70 1]: /images/20220504/65e0f2ee0ff24d5ab92a9c56f46e55da.png [https_jersey.github.io_documentation_latest_test-framework.html]: https://jersey.github.io/documentation/latest/test-framework.html [https_github.com_jersey_jersey_tree_master_test-framework]: https://github.com/jersey/jersey/tree/master/test-framework [https_stackoverflow.com_questions_36348675_unable-to-test-jax-rs-with-json-entity]: https://stackoverflow.com/questions/36348675/unable-to-test-jax-rs-with-json-entity [https_stackoverflow.com_questions_48337023_eclipse-jerseytest-getclient-returns-resteasyclient]: https://stackoverflow.com/questions/48337023/eclipse-jerseytest-getclient-returns-resteasyclient [https_github.com_ContainX_openstack4j_blob_3.1.0_connectors_resteasy_pom.xml]: https://github.com/ContainX/openstack4j/blob/3.1.0/connectors/resteasy/pom.xml [https_github.com_resteasy_Resteasy_blob_master_resteasy-client_src_main_resources_META-INF_services_javax.ws.rs.client.ClientBuilder]: https://github.com/resteasy/Resteasy/blob/master/resteasy-client/src/main/resources/META-INF/services/javax.ws.rs.client.ClientBuilder [https_github.com_jersey_jersey_tree_master_core-client_src_main]: https://github.com/jersey/jersey/tree/master/core-client/src/main
还没有评论,来说两句吧...