I have a problem with setting MessageBodyReader - I tried to set it according to a number of tutorials, however its functions are never called. I am using Jersey 2.27
This is my MessageBodyReader implementation:
@Provider
public class MyMsgBodyReader implements MessageBodyReader<Object> {
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
HashMap<String, String> hash = new HashMap<>();
hash.put("replaced", "true");
return hash;
}
}
And i try to call it like this:
@Path("api")
@ApplicationScoped
public class TestClazz {
@GET
@Path("test")
public Response test() {
Client client = ClientBuilder.newBuilder().register(MyMsgBodyReader.class).build();
WebTarget target = client.target("http://localhost:3000").path("/api/test");
Response resp = target.request().get();
Object receivedEntity = resp.getEntity();
return Response.ok(receivedEntity).build();
}
}
While receivedEntity should be hashmap with one entry, it is still the original entity I received from my test api. I even tried to set breakpoints inside of MyMsgBodyReader methods, and confirmed they are not called even once.
I also tried scanning for it in multiple ways:
@ApplicationPath("v1")
public class App extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(MyMsgBodyReader.class);
classes.add(
return classes;
}
@Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new MsgBodiReader());
return singletons;
}
}
I don't know what else to try.
The first thing you need to decide is what type of data the reader will handle. You do this with the @Consumes
annotation. You put this on top of the reader class. Based on the Content-Type
of the response, Jersey will select the correct reader(s) that matches the media type in the @Consumes
. If there are multiple readers that match, then the next step is to test the isReadable()
method. If more than one pass this check, then Jersey will check the priority. You can add a @Priority(int)
annotation on the reader. Or you can pass the priority as a second argument to the register()
method. The higher priority will have the most precedence. If you are using a media type in the @Consumes
for which there is already a standard reader, then you probably will want to use the @Priority
so that yours will be used.
If the reader is still not called, you should check the Content-Type
of the response and make sure it is what you expect. You might also set the Accept
header (which can be implicitly set by passing a media type to the request()
method.
Here is an example for which your reader would be called.
@Provider
@Conumes("application/custom")
public class MyReader implements MessageBodyReader<String> {
}
Response res = client.target("test")
.register(MyReader.class)
.request("application/custom")
.get();
Assuming that the server does return data of type application/custom
, this reader should be called. If the server is not capable of returning the data format, and you request that type, then you should get a 406 Not Acceptable error.
Also you should not get the returned data with Response#getEntity()
. You should use readEntity(Class)
, passing in the Java type you want the data converted to (uses MessageBodyReader
under the hood). And for generics, you should use GenericType
Map<String, String> data = res.readEntity(new GenericType<Map<String, String>>(){});
Here's a complete test using Jersey Test Framework.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CustomReaderTest extends JerseyTest {
private static final String DATA = "UselessStaticData";
private static final String CUSTOM_MEDIA_TYPE = "application/useless";
@Consumes(CUSTOM_MEDIA_TYPE)
public static class UselessReader implements MessageBodyReader<String> {
@Override
public boolean isReadable(Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public String readFrom(Class<String> aClass, Type type, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String,String> multivaluedMap,
InputStream inputStream) throws IOException, WebApplicationException {
return DATA;
}
}
@Path("test")
public static class TestResource {
@GET
@Produces(CUSTOM_MEDIA_TYPE)
public InputStream post(String data) {
return new ByteArrayInputStream("Test".getBytes(StandardCharsets.UTF_8));
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class);
}
@Test
public void testIt() {
Response res = target("test")
.register(UselessReader.class)
.request(CUSTOM_MEDIA_TYPE)
.get();
assertThat(res.readEntity(String.class)).isEqualTo(DATA);
}
}