如何使用 Spring MockMvc 放置多部分/表单数据?

Posted

技术标签:

【中文标题】如何使用 Spring MockMvc 放置多部分/表单数据?【英文标题】:How to PUT multipart/form-data using Spring MockMvc? 【发布时间】:2016-11-29 00:42:39 【问题描述】:

我有一个带有PUT 方法的控制器方法,它接收multipart/form-data:

   @RequestMapping(value = "/putIn", method = RequestMethod.PUT)
   public Foo updateFoo(HttpServletRequest request,
                           @RequestBody Foo foo,
                           @RequestParam("foo_icon") MultipartFile file) 
    ...
   

我想使用MockMvc 对其进行测试。不幸的是,MockMvcRequestBuilders.fileUpload 本质上创建了一个 MockMultipartHttpServletRequestBuilder 的实例,它有一个 POST 方法:

super(HttpMethod.POST, urlTemplate, urlVariables)

编辑: 我当然可以 我不能创建我自己的MockHttpServletRequestBuilder 实现,比如说

public MockPutMultipartHttpServletRequestBuilder(String urlTemplate, Object... urlVariables) 
    super(HttpMethod.PUT, urlTemplate, urlVariables);
    super.contentType(MediaType.MULTIPART_FORM_DATA);

因为MockHttpServletRequestBuilder 有一个包本地构造函数。

但我想知道有没有更方便的有什么办法可以做到这一点,可能我错过了一些现有的类或方法吗?

【问题讨论】:

您能否将我的答案标记为已接受的答案?这将有助于保持 *** 的清洁和高效。谢谢! 【参考方案1】:

是的,有办法,而且也很简单!

我自己也遇到了同样的问题。虽然我对 Sam Brannen 的回答感到灰心,但似乎 Spring MVC 现在确实支持 PUT 文件上传,因为我可以使用 Postman 简单地执行这样的请求(我使用的是 Spring Boot 1.4.2)。所以,我一直在挖掘,发现唯一的问题是MockMvcRequestBuilders.fileUpload() 返回的MockMultipartHttpServletRequestBuilder 具有硬编码为“POST”的方法。然后我发现了with()方法……

这让我想出了这个巧妙的小技巧来强制MockMultipartHttpServletRequestBuilder 无论如何都要使用“PUT”方法:

    MockMultipartFile file = new MockMultipartFile("data", "dummy.csv",
            "text/plain", "Some dataset...".getBytes());

    MockMultipartHttpServletRequestBuilder builder =
            MockMvcRequestBuilders.fileUpload("/test1/datasets/set1");
    builder.with(new RequestPostProcessor() 
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) 
            request.setMethod("PUT");
            return request;
        
    );
    mvc.perform(builder
            .file(file))
            .andExpect(status().isOk());

像魅力一样工作!

【讨论】:

这应该是问题的解决方案,因为它可以解决提出问题的人的问题 仅供参考 - 在春季 5 中,fileUpload 已弃用,应改用 multipart。除此之外 - 就像一个魅力:) 使用 java 8 lambda 语法:builder.with(request -> request.setMethod("PUT"); return request; ); PUT 字符串也可以从此常量中检索:HttpMethod.PUT.name()【参考方案2】:

不幸的是,Spring MVC 测试目前不支持此功能,除了创建您自己的自定义 MockPutMultipartHttpServletRequestBuilder 并从标准实现中复制粘贴代码之外,我没有看到其他解决方法。

不管怎样,Spring MVC 默认也不支持PUT 文件上传请求。 Multipart 解析器经过硬编码,仅接受 POST 文件上传请求——既适用于 Apache Commons,也适用于标准 Servlet API 支持。

如果您还希望 Spring 支持 PUT 请求,请随时在 Spring 的 JIRA 问题跟踪器中发送 open a ticket。

【讨论】:

我找到了一个简单的解决方案!看看我的回答 Spring 现在还支持任何方法,而不仅仅是 POST。见this commit【参考方案3】:

为 Kotlin 翻译 @HammerNl 答案。这对我有用。

val file = File("/path/to/file").readBytes()
val multipartFile = MockMultipartFile("image", "image.jpg", "image/jpg", file)

val postProcess = RequestPostProcessor  it.method = "PUT"; it
mockMvc.perform(
    MockMvcRequestBuilders.multipart("/api/image/$id")
        .file(multipartFile)
        .with(postProcess))
        .andExpect(MockMvcResultMatchers.status().isOk)

【讨论】:

【参考方案4】:

foofile 都可以通过

尝试重写你的控制器:

@RequestMapping(value = "/putIn", method = RequestMethod.PUT)
public Foo updateFoo(
    HttpServletRequest request,
    @RequestPart Foo foo,
    @RequestPart MultipartFile file) 
    ...

测试看起来像:

    MockMultipartFile file = new MockMultipartFile("file", "dummy.csv",
            "text/plain", "Some dataset...".getBytes());
    // application/json if you pass json as string
    MockMultipartFile file2 = new MockMultipartFile("foo", "foo.txt",
            "application/json", "Foo data".getBytes());

    MockMultipartHttpServletRequestBuilder builder =
            MockMvcRequestBuilders.multipart("/test1/datasets/set1");
    builder.with(new RequestPostProcessor() 
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) 
            request.setMethod("PUT");
            return request;
        
    );
    mvc.perform(builder
            .file(file)
            .file(file2))
            .andExpect(status().ok());

【讨论】:

你好。刚刚尝试这样做,但我不断收到“不支持内容类型'应用程序/json'”以进行 mockMvc 测试。我有一个带有两个 RequestPart 的控制器,其中一个是 POJO,另一个是 MultipartFile。我做错了什么?使用邮递员工作正常!

以上是关于如何使用 Spring MockMvc 放置多部分/表单数据?的主要内容,如果未能解决你的问题,请参考以下文章

Spring MockMvc 和异步控制器的 HTTP 状态码

如何使用 MockMvc 测试弹簧控制器方法?

使用 Spring MockMVC 测试 Spring 的 @RequestBody

Spring 的 MockMvc 是用于单元测试还是集成测试?

MockMvc 和 Spring Security - Null FilterChainProxy

在 Spring Boot 1.4 MVC 测试中使用 @WebMvcTest 设置 MockMvc