我在一个小型Scala应用程序中使用spring-test和spring-boot。(很抱歉我的介绍很长,问题很短!)
到目前为止,一切都很顺利,直到我决定修改其中一个端点以支持流。为此,我将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测试的MockMvc中使用REST控制器时不再是这种情况。实际上,当通过MockMvc执行请求时,响应实际上比通常大4个字节。因此,一些简单的断言会失败:
@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
关闭。实际上,它附加了请求处理程序的返回值,该值是OutputStream
中的字节数(实际上来自IOUtils.closeQuietly
)。
为什么MockMvc附加到OutputStream
后,它已经关闭?这是一个错误,还是我使用库不正确?
根据返回类型、方法注释和某些情况下的输入参数,可以以不同的方式解释控制器方法的返回值。
在@RequestMapping注释和参考文档中详细列出了这一点。对于流的情况,将HttpServletResponse作为输入参数的组合(顺便也可以使用OutputStream)和void作为返回类型,向Spring MVC表明您已经自己处理了响应。