我为JSON编写了一个自定义消息体编写器和读取器,而不是与Jersey打包的库。我试图用简单的Java服务和测试类来测试它。我遇到了一些问题,因为测试客户端因为找不到原因而永远不会看到JSON主体。启用日志记录工具后,我发现服务器似乎正确发送了JSON数据:
1:46:03 PM com.sun.jersey.api.container.filter.LoggingFilter$Adapter finish
INFO: 1 * Server out-bound response
1 < 200
1 < Content-Type: application/json
1 <
{"id":1,"name":"Business Intelligence Team","parentTeam":{"id":2,"name":"Data Analysis Team"}}
但客户方从未收到这样的身体:
1:46:03 PM com.sun.jersey.api.client.filter.LoggingFilter log
INFO: 1 * Client in-bound response
1 < 200
1 < Content-Length: 0
1 < Date: Wed, 12 Feb 2014 18:46:03 GMT
1 < server: grizzly/2.2.16
1 < Content-Type: application/json
1 <
调试Jersey库,我看到确实,InputStream
自动设置在默认的EmptyInputStream
对象上,好像这从未正确设置,或者至少不承认任何输入http体。调试我的自定义提供程序,它们似乎运行良好并且行为正确。
我使用泽西岛版本1.18。这是我的测试类:
public class DeployResourceTest extends JerseyTest {
private WebResource resource;
@BeforeClass
public static void setupJerseyLog() throws Exception {
Handler fh = new ConsoleHandler(); // FileHandler("/tmp/jersey_test.log");
Logger.getLogger("").addHandler(fh);
Logger.getLogger("com.sun.jersey").setLevel(Level.FINEST);
}
public DeployResourceTest() {
super(new WebAppDescriptor.Builder("com.deploymentnow.dnagent.services.rest")
.contextParam("contextConfigLocation", "classpath:applicationContext.xml")
.contextParam("log4jConfigLocation", "src/main/webapp/WEB-INF/log4j.properties")
.contextParam("webAppRootKey", "template-jersey-spring-jpa.root")
.initParam("com.sun.jersey.config.property.packages", "com.deploymentnow.agent.services.rest")
.initParam("com.sun.jersey.config.feature.Trace", "true")
.initParam("jersey.config.server.provider.classnames", "org.glassfish.jersey.filter.LoggingFilter")
.initParam("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.LoggingFilter")
.initParam("com.sun.jersey.spi.container.ContainerResponseFilters", "com.sun.jersey.api.container.filter.LoggingFilter")
.clientConfig(new DefaultClientConfig(PlayJsonMessageBodyReader.class, PlayJsonMessageBodyWriter.class))
.contextListenerClass(Log4jConfigListener.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.servletClass(SpringServlet.class).build());
this.resource = resource();
resource.addFilter(new LoggingFilter());
}
@Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
@Test
public void testListResources() {
ClientResponse response = resource.path("application/26/team").accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);
Team team = response.getEntity(Team.class);
Assert.assertNotNull(team);
}
}
我的网络服务,非常小:
@Path("application/{application}")
public class DeployResource {
@Path("team")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response testTeam() {
Team team = new Team(1, "Business Intelligence Team", Option.<Team>apply(new Team(2, "Data Analysis Team", Option.<Team>empty())));
return Response.status(200).entity(team).build();
}
}
邮件正文撰稿人。它目前实现了一种非常特定的类型,因为它仅用于测试:
@Provider
@Produces("application/json")
public class PlayJsonMessageBodyWriter implements MessageBodyWriter<Team> {
@Override
public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == Team.class && MediaType.APPLICATION_JSON_TYPE.equals(mediaType);
}
@Override
public long getSize(Team o, Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
// deprecated by JAX-RS 2.0 and ignored by Jersey runtime
return 0;
}
@Override
public void writeTo(Team team, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
JsValue value = Json.toJson(team, DeploymentFormat.teamFormat());
String str = value.toString();
byte[] bytes = str.getBytes(Charset.forName("UTF-8"));
entityStream.write(bytes);
}
}
我正在直接在实体流中编写,如文档和教程所示..以及消息正文阅读器:
@Provider
@Consumes("application/json")
public class PlayJsonMessageBodyReader implements MessageBodyReader<Team> {
@Override
public boolean isReadable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type == Team.class && MediaType.APPLICATION_JSON_TYPE.equals(mediaType);
}
@Override
public Team readFrom(Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
String body = IOUtils.toString(entityStream, "UTF-8");
if (!hasText(body)) {
return null;
}
JsValue json = Json.parse(body);
JsResult<Team> result = Json.fromJson(json, DeploymentFormat.teamFormat());
return result.get();
}
}
我还尝试使用REST客户端查询部署在Tomcat servlet容器中的Web服务,但我没有响应,尽管日志记录似乎另有说明。我想知道日志记录是否以某种方式消耗了内容体..但即使禁用日志记录,它也无法正常工作。
更新
我能够使我的工作正常,但不是通过直接解决我的问题,而是通过将JAX-RS实现从 Jersey 切换到 Apache CXF 。代码没有区别;它只是有效。
我看到的主要区别是使用的输出流类型。在我的提供程序中,Jersey向我发送了一些CommittingOutputStream
参数,它似乎具有更多的抽象性和一点点敏感性。例如,我注意到在其上调用flush
方法会使流的内容完全消失,甚至无法出现在日志记录中。
对于Apache CXF,输出流类型为CachedOutputStream
。它的行为与预期的一样,内容实际上是发送给客户端的。
我还没有找到为什么它在 Jersey 中不起作用。只需转储它并获得完成工作的东西就更快了,a.k.a。 Apache CXF 。
如果泽西队的某个人能够回答这种不稳定的行为,我会打开这个问题。