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 表明您自己处理了响应。