MockMvc 和流端点 - 流关闭后的额外字节
MockMvc and streaming endpoints - additional bytes after stream close
我在小型 Scala 应用程序中使用 spring-test 和 spring-boot。 (很抱歉 intro/short 的问题!)
到目前为止,一切正常,直到我决定修改其中一个端点以支持流式传输。为此,我将 HttpServletResponse
对象添加到我的请求处理程序,并使用 Apache Commons 的 IOUtils.copy
.
复制源数据
@RequestMapping(value = Array("/hello"), method = Array(RequestMethod.GET))
def retrieveFileForVersion(response:HttpServletResponse) = {
val is = getAnInputStream
val os = response.getOutputStream
try {
IOUtils.copy(is, os)
} finally {
IOUtils.closeQuietly(is)
os.flush()
IOUtils.closeQuietly(os)
}
}
}
这似乎工作得很好。我可以从端点检索二进制数据并验证其 MD5 校验和是否与源数据的 MD5 校验和匹配。
但是,我注意到在 spring-test 的 MockMvc 中使用 REST 控制器时不再是这种情况。事实上,当通过 MockMvc 执行请求时,响应实际上比平时大四个字节。因此,一些简单的断言失败了:
@Test
def testHello() = {
// ... snip ... read the binary file into a byte array
val bytes = IOUtils.toByteArray(...)
val result = mockMvc.perform(get("/hello")).andExpect(status.isOk).andReturn
val responseLength = result.getResponse.getContentAsByteArray.length
// TODO - find out why this test fails!
assert(responseLength == bytes.length, s"Response size: $responseLength, file size: ${bytes.length}")
assert(Arrays.equals(result.getResponse.getContentAsByteArray, bytes))
}
使用调试器,我能够确定 MockMvc 正在附加到响应 OutputStream
,即使它已经使用 IOUtils.closeQuietly
关闭。事实上,它附加了请求处理程序的 return 值,即 OutputStream
中的字节数(实际上来自 IOUtils.closeQuietly
)。
为什么 MockMvc 在 OutputStream
已经关闭后附加到它?这是一个错误,还是我错误地使用了库?
控制器方法中的 return 值可以根据 return 类型、方法注释以及某些情况下的输入参数以不同方式解释。
@RequestMapping 注释和参考文档中详尽列出了这一点。对于您的流媒体案例,将 HttpServletResponse 的组合作为输入参数(您也可以顺便采用 OutputStream)和 void 作为 return 类型,向 Spring MVC 表明您自己处理了响应。
我在小型 Scala 应用程序中使用 spring-test 和 spring-boot。 (很抱歉 intro/short 的问题!)
到目前为止,一切正常,直到我决定修改其中一个端点以支持流式传输。为此,我将 HttpServletResponse
对象添加到我的请求处理程序,并使用 Apache Commons 的 IOUtils.copy
.
@RequestMapping(value = Array("/hello"), method = Array(RequestMethod.GET))
def retrieveFileForVersion(response:HttpServletResponse) = {
val is = getAnInputStream
val os = response.getOutputStream
try {
IOUtils.copy(is, os)
} finally {
IOUtils.closeQuietly(is)
os.flush()
IOUtils.closeQuietly(os)
}
}
}
这似乎工作得很好。我可以从端点检索二进制数据并验证其 MD5 校验和是否与源数据的 MD5 校验和匹配。
但是,我注意到在 spring-test 的 MockMvc 中使用 REST 控制器时不再是这种情况。事实上,当通过 MockMvc 执行请求时,响应实际上比平时大四个字节。因此,一些简单的断言失败了:
@Test
def testHello() = {
// ... snip ... read the binary file into a byte array
val bytes = IOUtils.toByteArray(...)
val result = mockMvc.perform(get("/hello")).andExpect(status.isOk).andReturn
val responseLength = result.getResponse.getContentAsByteArray.length
// TODO - find out why this test fails!
assert(responseLength == bytes.length, s"Response size: $responseLength, file size: ${bytes.length}")
assert(Arrays.equals(result.getResponse.getContentAsByteArray, bytes))
}
使用调试器,我能够确定 MockMvc 正在附加到响应 OutputStream
,即使它已经使用 IOUtils.closeQuietly
关闭。事实上,它附加了请求处理程序的 return 值,即 OutputStream
中的字节数(实际上来自 IOUtils.closeQuietly
)。
为什么 MockMvc 在 OutputStream
已经关闭后附加到它?这是一个错误,还是我错误地使用了库?
控制器方法中的 return 值可以根据 return 类型、方法注释以及某些情况下的输入参数以不同方式解释。
@RequestMapping 注释和参考文档中详尽列出了这一点。对于您的流媒体案例,将 HttpServletResponse 的组合作为输入参数(您也可以顺便采用 OutputStream)和 void 作为 return 类型,向 Spring MVC 表明您自己处理了响应。