'Illegal attempt to call getOutputStream() after getWriter() has already been called' 泽西岛测试期间
'Illegal attempt to call getOutputStream() after getWriter() has already been called' during Jersey test
我有一个正在尝试测试的 Jersey 端点。
端点:
@Path("image")
class ImageResource {
@Inject
private ImageService imageService;
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response save(ImageVO imageVO) {
imageVO = imageService.save(imageVO);
ResponseVO responseVO = new ResponseVO(true, imageVO);
return Response.status(Response.Status.CREATED).entity(responseVO).build();
}
}
测试:
class ImageResourceTest extends JerseyTest {
private static final String BASE_ENDPOINT = "rest";
private static final String IMAGE_ENDPOINT = "image";
@Mock
private ImageService imageService;
@InjectMocks
private ImageResource imageResource;
@Override
protected URI getBaseUri() {
return UriBuilder.fromUri(super.getBaseUri()).path(BASE_ENDPOINT).build();
}
@Override
protected Application configure() {
MockitoAnnotations.initMocks(this);
ResourceConfig config = new ResourceConfig();
config.register(imageResource);
return config;
}
@Test
public void testSave() {
ImageVO imageVO = new ImageVO();
imageVO.setId("TEST");
when(imageService.save(imageVO)).thenReturn(imageVO);
Entity e = Entity.entity(imageVO, MediaType.APPLICATION_JSON);
Response response = target().path(IMAGE_ENDPOINT).request().post(e, Response.class);
assertEquals(201, response.getStatus());
ResponseVO responseVO = response.readEntity(ResponseVO.class);
assertTrue(responseVO.isSuccess());
}
}
例外情况:
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer start
INFO: Starting GrizzlyTestContainer...
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9998]
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.server.ServerRuntime$Responder process
SEVERE: Error occurred when processing a response created from an already mapped exception.
InboundJaxrsResponse{ClientResponse{method=POST, uri=http://localhost:9998/rest/image, status=500, reason=Request failed.}}
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.server.ServerRuntime$Responder release
WARNING: Attempt to release request processing resources has failed for a request.
java.lang.IllegalStateException: Illegal attempt to call getOutputStream() after getWriter() has already been called.
at org.glassfish.grizzly.http.server.Response.getNIOOutputStream(Response.java:621)
at org.glassfish.grizzly.http.server.Response.getOutputStream(Response.java:646)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer$ResponseWriter.writeResponseStatusAndHeaders(GrizzlyHttpContainer.java:265)
at org.glassfish.jersey.server.ServerRuntime$Responder.getOutputStream(ServerRuntime.java:561)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.flushBuffer(CommittingOutputStream.java:305)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:261)
at org.glassfish.jersey.message.internal.CommittingOutputStream.close(CommittingOutputStream.java:276)
at org.glassfish.jersey.message.internal.OutboundMessageContext.close(OutboundMessageContext.java:835)
at org.glassfish.jersey.server.ContainerResponse.close(ContainerResponse.java:411)
at org.glassfish.jersey.server.ServerRuntime$Responder.release(ServerRuntime.java:667)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:438)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:265)
at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
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:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:319)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1028)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:363)
at org.glassfish.grizzly.http.server.HttpHandler.run(HttpHandler.java:217)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:619)
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer stop
INFO: Stopping GrizzlyTestContainer...
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9998]
这几天我一直在用头撞墙,但找不到解决办法。我有多个以相同方式测试的其他端点,没有这个问题。我将 Jersey 2.6 与 Java 6u5.
一起使用
我不知道,我无法使用您提供的内容重现确切的问题(尽管我确实遇到了 一些 错误)。我只是 post 我测试过的东西,也许你能弄明白。
我所做的主要更改是:
- 刚刚使用了 Jersey 的注入框架来处理 DI。你可以在下面我
config.register(new AbstractBinder()
的地方看到它。查看更多相关信息 here
- 将 Mock 的
when
移动到配置点,而不是在测试方法中。
如果您仍然无法让它工作,请尝试仅使用我提供的依赖项和一个测试用例来创建一个新项目。如果它有效,那么它就是你没有向我们展示的东西,这可能是问题所在。如果你想提供一个重现问题的小 Github 项目,我可以看看它。
Maven 依赖项
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
测试用例
import java.net.URI;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.junit.Assert.*;
import org.mockito.Mockito;
import static org.mockito.Mockito.when;
public class ImageServiceTest extends JerseyTest {
private static final String BASE_ENDPOINT = "rest";
private static final String IMAGE_ENDPOINT = "image";
private static final ImageVO imageVO = new ImageVO("Test");
public static interface ImageService {
ImageVO save(ImageVO imageVo);
}
public static class ImageVO {
public String id;
public ImageVO() {}
public ImageVO(String id) { this.id = id; }
}
public static class ResponseVO {
public boolean cool;
public ImageVO imageVo;
public ResponseVO() {}
public ResponseVO(boolean cool, ImageVO imageVo) {
this.cool = cool; this.imageVo = imageVo;
}
}
@Path("image")
public static class ImageResource {
@Inject
private ImageService imageService;
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response save(ImageVO imageVO) {
imageVO = imageService.save(imageVO);
ResponseVO responseVO = new ResponseVO(true, imageVO);
return Response.status(Response.Status.CREATED).entity(responseVO).build();
}
}
@Override
public Application configure() {
final ImageService imageService = Mockito.mock(ImageService.class);
when(imageService.save(imageVO)).thenReturn(imageVO);
ResourceConfig config = new ResourceConfig();
config.register(JacksonJsonProvider.class);
config.register(ImageResource.class);
config.register(new AbstractBinder(){
@Override
protected void configure() {
bind(imageService).to(ImageService.class);
}
});
return config;
}
@Override
protected URI getBaseUri() {
return UriBuilder.fromUri(super.getBaseUri()).path(BASE_ENDPOINT).build();
}
@Test
public void testSave() {
Entity e = Entity.entity(imageVO, MediaType.APPLICATION_JSON);
Response response = target().path(IMAGE_ENDPOINT).request().post(e, Response.class);
assertEquals(201, response.getStatus());
ResponseVO responseVO = response.readEntity(ResponseVO.class);
assertTrue(responseVO.cool);
response.close();
}
}
添加并注册 ExceptionMapper
允许我捕获并输出抛出的错误:
Unrecognized field "name" (Class com.example.ImageVO), not marked as ignorable at [Source:org.glassfish.jersey.message.internal.EntityInputStream@13b7c28; line: 1, column: 149] (through reference chain: com.example.ImageVO["name"])
这是因为我的 ImageVO
class 有一个名为 getName
的 public 方法,但没有 name
属性 . POST
ing ImageVO
实例导致 JSON 有一个值为 null
的 name
键,创建异常。
对此有两个解决方案:
- 将 Jackson 的
DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES
属性 设置为 false。
在测试中为 post 实体使用实际的 JSON 字符串:
Entity e = Entity.entity("{\"id\":\"TEST\"}", MediaType.APPLICATION_JSON);
Response r = target().path(IMAGE_ENDPOINT).request.post(e, Response.class);
我有一个正在尝试测试的 Jersey 端点。
端点:
@Path("image")
class ImageResource {
@Inject
private ImageService imageService;
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response save(ImageVO imageVO) {
imageVO = imageService.save(imageVO);
ResponseVO responseVO = new ResponseVO(true, imageVO);
return Response.status(Response.Status.CREATED).entity(responseVO).build();
}
}
测试:
class ImageResourceTest extends JerseyTest {
private static final String BASE_ENDPOINT = "rest";
private static final String IMAGE_ENDPOINT = "image";
@Mock
private ImageService imageService;
@InjectMocks
private ImageResource imageResource;
@Override
protected URI getBaseUri() {
return UriBuilder.fromUri(super.getBaseUri()).path(BASE_ENDPOINT).build();
}
@Override
protected Application configure() {
MockitoAnnotations.initMocks(this);
ResourceConfig config = new ResourceConfig();
config.register(imageResource);
return config;
}
@Test
public void testSave() {
ImageVO imageVO = new ImageVO();
imageVO.setId("TEST");
when(imageService.save(imageVO)).thenReturn(imageVO);
Entity e = Entity.entity(imageVO, MediaType.APPLICATION_JSON);
Response response = target().path(IMAGE_ENDPOINT).request().post(e, Response.class);
assertEquals(201, response.getStatus());
ResponseVO responseVO = response.readEntity(ResponseVO.class);
assertTrue(responseVO.isSuccess());
}
}
例外情况:
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer start
INFO: Starting GrizzlyTestContainer...
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9998]
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.server.ServerRuntime$Responder process
SEVERE: Error occurred when processing a response created from an already mapped exception.
InboundJaxrsResponse{ClientResponse{method=POST, uri=http://localhost:9998/rest/image, status=500, reason=Request failed.}}
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.server.ServerRuntime$Responder release
WARNING: Attempt to release request processing resources has failed for a request.
java.lang.IllegalStateException: Illegal attempt to call getOutputStream() after getWriter() has already been called.
at org.glassfish.grizzly.http.server.Response.getNIOOutputStream(Response.java:621)
at org.glassfish.grizzly.http.server.Response.getOutputStream(Response.java:646)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer$ResponseWriter.writeResponseStatusAndHeaders(GrizzlyHttpContainer.java:265)
at org.glassfish.jersey.server.ServerRuntime$Responder.getOutputStream(ServerRuntime.java:561)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.flushBuffer(CommittingOutputStream.java:305)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:261)
at org.glassfish.jersey.message.internal.CommittingOutputStream.close(CommittingOutputStream.java:276)
at org.glassfish.jersey.message.internal.OutboundMessageContext.close(OutboundMessageContext.java:835)
at org.glassfish.jersey.server.ContainerResponse.close(ContainerResponse.java:411)
at org.glassfish.jersey.server.ServerRuntime$Responder.release(ServerRuntime.java:667)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:438)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:265)
at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
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:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:319)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1028)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:363)
at org.glassfish.grizzly.http.server.HttpHandler.run(HttpHandler.java:217)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
at java.lang.Thread.run(Thread.java:619)
Jul 13, 2015 9:14:28 AM org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory$GrizzlyTestContainer stop
INFO: Stopping GrizzlyTestContainer...
Jul 13, 2015 9:14:28 AM org.glassfish.grizzly.http.server.NetworkListener shutdownNow
INFO: Stopped listener bound to [localhost:9998]
这几天我一直在用头撞墙,但找不到解决办法。我有多个以相同方式测试的其他端点,没有这个问题。我将 Jersey 2.6 与 Java 6u5.
一起使用我不知道,我无法使用您提供的内容重现确切的问题(尽管我确实遇到了 一些 错误)。我只是 post 我测试过的东西,也许你能弄明白。
我所做的主要更改是:
- 刚刚使用了 Jersey 的注入框架来处理 DI。你可以在下面我
config.register(new AbstractBinder()
的地方看到它。查看更多相关信息 here - 将 Mock 的
when
移动到配置点,而不是在测试方法中。
如果您仍然无法让它工作,请尝试仅使用我提供的依赖项和一个测试用例来创建一个新项目。如果它有效,那么它就是你没有向我们展示的东西,这可能是问题所在。如果你想提供一个重现问题的小 Github 项目,我可以看看它。
Maven 依赖项
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
测试用例
import java.net.URI;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.junit.Assert.*;
import org.mockito.Mockito;
import static org.mockito.Mockito.when;
public class ImageServiceTest extends JerseyTest {
private static final String BASE_ENDPOINT = "rest";
private static final String IMAGE_ENDPOINT = "image";
private static final ImageVO imageVO = new ImageVO("Test");
public static interface ImageService {
ImageVO save(ImageVO imageVo);
}
public static class ImageVO {
public String id;
public ImageVO() {}
public ImageVO(String id) { this.id = id; }
}
public static class ResponseVO {
public boolean cool;
public ImageVO imageVo;
public ResponseVO() {}
public ResponseVO(boolean cool, ImageVO imageVo) {
this.cool = cool; this.imageVo = imageVo;
}
}
@Path("image")
public static class ImageResource {
@Inject
private ImageService imageService;
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response save(ImageVO imageVO) {
imageVO = imageService.save(imageVO);
ResponseVO responseVO = new ResponseVO(true, imageVO);
return Response.status(Response.Status.CREATED).entity(responseVO).build();
}
}
@Override
public Application configure() {
final ImageService imageService = Mockito.mock(ImageService.class);
when(imageService.save(imageVO)).thenReturn(imageVO);
ResourceConfig config = new ResourceConfig();
config.register(JacksonJsonProvider.class);
config.register(ImageResource.class);
config.register(new AbstractBinder(){
@Override
protected void configure() {
bind(imageService).to(ImageService.class);
}
});
return config;
}
@Override
protected URI getBaseUri() {
return UriBuilder.fromUri(super.getBaseUri()).path(BASE_ENDPOINT).build();
}
@Test
public void testSave() {
Entity e = Entity.entity(imageVO, MediaType.APPLICATION_JSON);
Response response = target().path(IMAGE_ENDPOINT).request().post(e, Response.class);
assertEquals(201, response.getStatus());
ResponseVO responseVO = response.readEntity(ResponseVO.class);
assertTrue(responseVO.cool);
response.close();
}
}
添加并注册 ExceptionMapper
允许我捕获并输出抛出的错误:
Unrecognized field "name" (Class com.example.ImageVO), not marked as ignorable at [Source:org.glassfish.jersey.message.internal.EntityInputStream@13b7c28; line: 1, column: 149] (through reference chain: com.example.ImageVO["name"])
这是因为我的 ImageVO
class 有一个名为 getName
的 public 方法,但没有 name
属性 . POST
ing ImageVO
实例导致 JSON 有一个值为 null
的 name
键,创建异常。
对此有两个解决方案:
- 将 Jackson 的
DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES
属性 设置为 false。 在测试中为 post 实体使用实际的 JSON 字符串:
Entity e = Entity.entity("{\"id\":\"TEST\"}", MediaType.APPLICATION_JSON); Response r = target().path(IMAGE_ENDPOINT).request.post(e, Response.class);