如何使用Spring MockMvc发送multipart/form-data请求?

42

我有一个控制器方法,使用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具有包局部构造函数。

但我想知道是否有更方便的方法来做到这一点,也许我错过了某个用于执行此操作的现有类或方法?


请问您能否将我的答案标记为采纳的答案?这会有助于保持 StackOverflow 的干净和高效。谢谢! - HammerNL
5个回答

94

是的,有一种方法,而且非常简单!

我自己也遇到了同样的问题。虽然 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.multipart("/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());

运行得非常好!


2
这应该是问题的解决方案,因为它允许解决提问者的问题。 - ftrujillo
4
FYI - 在Spring 5中,fileUpload已经被废弃,应该使用multipart代替。除此之外 - 一切正常 :) - Urosh T.
5
使用Java 8的Lambda语法: builder.with(request -> { request.setMethod("PUT"); return request; }); - user1075613
PUT字符串也可以从此常量中检索:HttpMethod.PUT.name() - Wim Deblauwe
花了半天时间,终于找到了这个答案,谢谢! - I have 10 fingers

5

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

值得一提的是,默认情况下,Spring MVC也不支持上传文件的PUT请求。多部分解析器被硬编码为仅接受POST请求进行文件上传,无论是Apache Commons还是标准Servlet API支持。

如果您希望Spring在此基础上支持PUT请求,请随时在Spring的JIRA问题跟踪器中打开一个工单


我找到了一个简单的解决方案!请查看我的答案。 - HammerNL
Spring现在也支持任何方法,而不仅仅是POST。请查看此提交。 - Wim Deblauwe

5

翻译 @HammerNl 给出的 Kotlin 回答。这个方法对我有效。

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)

0

你可以同时传递 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());

你好。我刚试图这样做,但是在mockMvc测试中一直收到“不支持内容类型'application/json'”的错误提示。我的控制器有两个RequestPart,其中一个是POJO,另一个是MultipartFile。我做错了什么?使用Postman却可以正常工作! - Lucas Soares

0
我认为这个问题在 V 5.3.22 版本中已经修复了。
/**
 * Variant of {@link #multipart(String, Object...)} that also accepts an
 * {@link HttpMethod}.
 * @param httpMethod the HTTP method to use
 * @param urlTemplate a URL template; the resulting URL will be encoded
 * @param uriVariables zero or more URI variables
 * @since 5.3.22
 */
public static MockMultipartHttpServletRequestBuilder multipart(HttpMethod httpMethod, String urlTemplate, Object... uriVariables) {
    return new MockMultipartHttpServletRequestBuilder(httpMethod, urlTemplate, uriVariables);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接