JaxRs 客户端不执行 MessageBodyReader
JaxRs Client doesn't execute MessageBodyReader
我在设置 MessageBodyReader 时遇到问题 - 我尝试根据许多教程进行设置,但从未调用过它的函数。我正在使用 Jersey 2.27
这是我的 MessageBodyReader 实现:
@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;
}
}
我试着这样称呼它:
@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();
}
}
虽然 receivedEntity 应该是只有一个条目的 hashmap,但它仍然是我从测试中收到的原始实体 api。我什至尝试在 MyMsgBodyReader 方法中设置断点,并确认它们一次都没有被调用。
我也试过用多种方式扫描它:
@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;
}
}
我不知道还能尝试什么。
您需要决定的第一件事是 reader 将处理什么类型的数据。您可以使用 @Consumes
注释来执行此操作。你把它放在 reader class 之上。根据响应的 Content-Type
,Jersey 将 select 与 @Consumes
中的媒体类型相匹配的正确 reader(s)。如果有多个reader匹配,那么下一步就是测试isReadable()
方法。如果不止一个通过此检查,则 Jersey 将检查优先级。您可以在 reader 上添加 @Priority(int)
注释。或者您可以将优先级作为第二个参数传递给 register()
方法。较高的优先级将具有最高的优先级。如果您在 @Consumes
中使用已经有标准 reader 的媒体类型,那么您可能希望使用 @Priority
以便使用您的媒体类型。
如果仍然没有调用reader,您应该检查Content-Type
of the response and make sure it is what you expect. You might also set the Accept
header(可以通过将媒体类型传递给request()
方法来隐式设置.
这是一个示例,您的 reader 将 调用。
@Provider
@Conumes("application/custom")
public class MyReader implements MessageBodyReader<String> {
}
Response res = client.target("test")
.register(MyReader.class)
.request("application/custom")
.get();
假设服务器做return类型application/custom
的数据,应该调用这个reader。如果服务器无法 returning 数据格式,而您请求该类型,那么您应该得到一个 406 Not Acceptable 错误。
此外,您不应使用 Response#getEntity()
获取 returned 数据。您应该使用 readEntity(Class)
,传入要将数据转换成的 Java 类型(在后台使用 MessageBodyReader
)。对于泛型,你应该使用 GenericType
Map<String, String> data = res.readEntity(new GenericType<Map<String, String>>(){});
这是使用 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);
}
}
我在设置 MessageBodyReader 时遇到问题 - 我尝试根据许多教程进行设置,但从未调用过它的函数。我正在使用 Jersey 2.27
这是我的 MessageBodyReader 实现:
@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;
}
}
我试着这样称呼它:
@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();
}
}
虽然 receivedEntity 应该是只有一个条目的 hashmap,但它仍然是我从测试中收到的原始实体 api。我什至尝试在 MyMsgBodyReader 方法中设置断点,并确认它们一次都没有被调用。
我也试过用多种方式扫描它:
@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;
}
}
我不知道还能尝试什么。
您需要决定的第一件事是 reader 将处理什么类型的数据。您可以使用 @Consumes
注释来执行此操作。你把它放在 reader class 之上。根据响应的 Content-Type
,Jersey 将 select 与 @Consumes
中的媒体类型相匹配的正确 reader(s)。如果有多个reader匹配,那么下一步就是测试isReadable()
方法。如果不止一个通过此检查,则 Jersey 将检查优先级。您可以在 reader 上添加 @Priority(int)
注释。或者您可以将优先级作为第二个参数传递给 register()
方法。较高的优先级将具有最高的优先级。如果您在 @Consumes
中使用已经有标准 reader 的媒体类型,那么您可能希望使用 @Priority
以便使用您的媒体类型。
如果仍然没有调用reader,您应该检查Content-Type
of the response and make sure it is what you expect. You might also set the Accept
header(可以通过将媒体类型传递给request()
方法来隐式设置.
这是一个示例,您的 reader 将 调用。
@Provider
@Conumes("application/custom")
public class MyReader implements MessageBodyReader<String> {
}
Response res = client.target("test")
.register(MyReader.class)
.request("application/custom")
.get();
假设服务器做return类型application/custom
的数据,应该调用这个reader。如果服务器无法 returning 数据格式,而您请求该类型,那么您应该得到一个 406 Not Acceptable 错误。
此外,您不应使用 Response#getEntity()
获取 returned 数据。您应该使用 readEntity(Class)
,传入要将数据转换成的 Java 类型(在后台使用 MessageBodyReader
)。对于泛型,你应该使用 GenericType
Map<String, String> data = res.readEntity(new GenericType<Map<String, String>>(){});
这是使用 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);
}
}