How to setup a Jersey client with Jackson (2.x) pr

2019-05-02 15:38发布

问题:

I'm struggling to setup a Jersey client for testing a POST request to a resource.

My Jersey and Jackson dependencies look like these:

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-grizzly2-http</artifactId>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey</groupId>
            <artifactId>jersey-bom</artifactId>
            <version>${jersey.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

I'm utilising Jersey 2.4 and Jackson 2.2.3.

I setup the Client amd WebTarget like this:

client = JerseyClientBuilder.newBuilder().build();

target = client.target(Main.BASE_URI);

I setup the Server like this:

ResourceConfig rc = new ResourceConfig().packages("com.my.package.is.here");
HttpServer httpServer = GrizzlyHttpServerFactory
                           .createHttpServer(URI.create(BASE_URI), rc);

I try to test the POST request at the resource:

ClientResponse response = target.path(resourceIdentifier)
                             .request(MediaType.APPLICATION_JSON_TYPE)
                             .post(Entity.json(testObjectJSON.toString()), 
                                       ClientResponse.class);

Currently, I'm getting the following error:

javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:849)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:768)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:96)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:761)
at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:90)
at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:671)
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:422)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:667)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:423)
at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:326)
at com.my.package.is.here.test.MyResourceTest.testPOSTJSON(MyResourceTest.java:117)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
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:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:272)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:247)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:146)
at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:305)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.findDeserializer(StdDeserializer.java:634)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:438)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:298)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:247)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:146)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectReader._findRootDeserializer(ObjectReader.java:1326)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1174)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:635)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:660)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:188)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:134)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:988)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:833)
... 38 more
Caused by: java.lang.IllegalArgumentException: Can not find a deserializer for non-concrete Map type [map type; class javax.ws.rs.core.MultivaluedMap, [simple type, class java.lang.String] -> [collection type; class java.util.List, contains [simple type, class java.lang.String]]]
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.createMapDeserializer(BasicDeserializerFactory.java:945)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:382)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:354)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:267)
... 55 more

However, when I'm testing the POST request at the resource with curl everything works fine. So I think it may has something to do with the Jersey client setup.

回答1:

It looks like that is has something to do with the implementation of the ClientResponse class, i.e., returning the result directly, didn't cause the exception. So the following lines of code are working to make the test successful:

String responseString = target.path(resourceIdentifier).request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(testObjectJSON.toStr‌​ing()), String.class);


回答2:

Try this:

package myco.mypkg;

import static org.junit.Assert.assertEquals;

import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.Test;
import org.springframework.http.MediaType;

/**
 *   Simple tests, with a simple endpoint/resource, no injections, to debug and document how JerseyTest works.
 *
 */
public class SimpleTest extends JerseyTest {

    public SimpleTest() {

        // Enable logging. This is really critical!
        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);     
    }

    /** Start a Grizzly server */
    @Override
    protected TestContainerFactory getTestContainerFactory() {
        return new GrizzlyWebTestContainerFactory();
    }

    /** Tell the server to deploy the TestResource endpoint class */
    @Override
    protected DeploymentContext configureDeployment() {
        return ServletDeploymentContext.forServlet(new ServletContainer(new ResourceConfig(TestResource.class))).build();
    }

    /**
     * Make a test call.
     */
    @Test
    public void testGet() {
        WebTarget target = target("root");

        String s = target.request().get(String.class);
        assertEquals("GET", s);
    }

    @Test
    public void testGetSub() {
        WebTarget target = target("root/sub");

        String s = target.request().get(String.class);
        assertEquals("sub", s);
    }

    @Test
    public void testPost() {
        WebTarget target = target("root/mypost");

        Form form = new Form();
        form.param("id", "1234");
        form.param("description", "test"); 
        String s = target.request(MediaType.APPLICATION_FORM_URLENCODED_VALUE).post(Entity.form(form), String.class);
        assertEquals("posted", s);
    }

    @Test
    public void testPostWithParam() {
        WebTarget target = target("root/postparams");

        Form form = new Form();
        form.param("id", "1234");
        String s = target.request(MediaType.APPLICATION_FORM_URLENCODED_VALUE).post(Entity.form(form), String.class);
        assertEquals("posted1234", s);
    }
}

These calling the following:

package myco.mypkg;

import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

/**
 *      A test endpoint, for use with SimpleTest 
 */
@Path("root")
public class TestResource {

    @GET
    public String get() {
        return "GET";
    }

    @Path("sub")
    @GET
    public String getSub() {
        return "sub";
    }

    @Path("mypost")
    @POST
    public String post() {
        return "posted";
    }

    /** If you omit the @FormParam, id contains "id=1234" */
    @Path("postparams")
    @POST
    public String post(@FormParam("id") String id) {
        return "posted" + id;
    }
}

I got the POST stuff by typing Entity. and waiting for the popup help to show me what the methods were.

I'm using Jersey 2.13.