How to use spring's MockMultipartHttpServletRequest? Getting "no multipart boundary was found"
Asked Answered
I

6

10

Clearly I'm not using this test fixture right. My servlet works just fine in tomcat, but when I try to use this mock, the multi-part boundary is not found. "the request was rejected because no multipart boundary was found".

There is an answer here that shows how to use this using a text file, but that answer sets the boundary string explicitly and embeds the file as test. I would think I would not need to do with by hand with methods like mockrequest.addFile(...)

What am I not setting here or how I am doing this wrong?

@org.testng.annotations.Test
public void testDoPost() throws Exception
{
    MockMultipartFile file = new MockMultipartFile("test.zip", "test.zip", "application/zip", MyServletTest.class.getResourceAsStream("/test.zip"));
    MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest();
    mockRequest.addFile(file);
    mockRequest.set
    mockRequest.setMethod("POST");
    mockRequest.setParameter("variant", "php");
    mockRequest.setParameter("os", "mac");
    mockRequest.setParameter("version", "3.4");
    MockHttpServletResponse response = new MockHttpServletResponse();
    new MyServletTest().doPost(mockRequest, response);
    //  BOOM !
}

Here is the exception

Caused by: blablah:   the request was rejected because no multipart boundary was found
Inerrant answered 25/3, 2014 at 17:26 Comment(3)
Please post the full stacktrace.Boring
Also, read the javadoc of setParameter.Boring
Also, we'll need to know what MyServletTest#doPost() actually does with the request.Champlain
T
21

You need to set the boundary.

Here there is a good explanations about what is the boundary https://mcmap.net/q/294006/-what-is-the-boundary-parameter-in-an-http-multi-part-post-request

To solve your problem try this code.

    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;

    import org.apache.commons.lang.ArrayUtils;
    import org.springframework.mock.web.MockHttpServletResponse;
    import org.springframework.mock.web.MockMultipartFile;
    import org.springframework.mock.web.MockMultipartHttpServletRequest;


public class FileUploadTest {

    public void testDoPost() throws IOException {
            Path path = Paths.get("c:\\temp\\test.zip");
            byte[] data = Files.readAllBytes(path);
            MockMultipartFile file = new MockMultipartFile("test.zip", "test.zip",
                    "application/zip", data);
            MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest();
            String boundary = "q1w2e3r4t5y6u7i8o9";
            mockRequest.setContentType("multipart/form-data; boundary="+boundary);
            mockRequest.setContent(createFileContent(data,boundary,"application/zip","test.zip"));
            mockRequest.addFile(file);
            mockRequest.setMethod("POST");
            mockRequest.setParameter("variant", "php");
            mockRequest.setParameter("os", "mac");
            mockRequest.setParameter("version", "3.4");
            MockHttpServletResponse response = new MockHttpServletResponse();
            new FileUpload().doPost(mockRequest, response);
        }

        public byte[] createFileContent(byte[] data, String boundary, String contentType, String fileName){
            String start = "--" + boundary + "\r\n Content-Disposition: form-data; name=\"file\"; filename=\""+fileName+"\"\r\n"
                     + "Content-type: "+contentType+"\r\n\r\n";;

            String end = "\r\n--" + boundary + "--"; // correction suggested @butfly 
            return ArrayUtils.addAll(start.getBytes(),ArrayUtils.addAll(data,end.getBytes()));
        }
}
Trehalose answered 30/5, 2015 at 2:38 Comment(2)
Is there any way to do this without manually inserting the boundary characters in the file? Most HTTP clients will do this kind of thing for you (curl, for instance), so I'm surprised that some combination of MockMultipartHttpServletRequest and MockMultipartFile doesn't get you this functionality for free.Lach
@Lach This is a good question. At that time I didn't find any method that did this automatically. It may be that in the new Spring releases there is already such a feature, after all this post is already 7 years old. I will try to find out about it.Trehalose
A
3

Vote up for Samuel. Though spent one day trying to make it working. The problem was in:

String end = "--" + boundary + "--";

Should be:

String end = "\r\n--" + boundary + "--";
Africa answered 8/2, 2017 at 9:42 Comment(0)
D
2

Great answer for Samuel, but a bug:

        String end = "\r\n"+ boundary + "--";

should be:

        String end = "--"+ boundary + "--";

Thanks for his work very much.

Deccan answered 22/6, 2015 at 18:36 Comment(2)
In fact it should be: String end = "--"+ boundary + "--";Deccan
@Trehalose Glad for your reply but please update your code again to remove the leading "\r\n" in end suffix string, or it will be appended to the original content of part, so that parsing like a integer form part causes exception.Deccan
P
1

You can create an HttpEntity using MultipartEntityBuilder, something like this:

HttpEntity httpEntity = MultipartEntityBuilder.create()
        .addTextBody("text-form-field", "Just a line of text.")
        .addTextBody("object-form-field", objectMapper.writeValueAsString(workerMetrics), ContentType.APPLICATION_JSON)
        .addBinaryBody("binary-format", "This would not really be text".getBytes())
        .build();

The post-process Spring's MockMultipartHttpServletRequestBuilder, adding the entity as the content. Copying over the entity's content type will set the boundaries for you.

mvc.perform(multipart("/upload")
        .with(request -> {
            try {
                request.setContent(httpEntity.getContent().readAllBytes());
                request.setContentType(httpEntity.getContentType().getValue());
                return request;
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        })
).andExpect(status().isAccepted());
Precedential answered 24/10, 2023 at 15:13 Comment(0)
S
0

Able to add multiple fields ,

   private byte[] createFileContents(String requestId, String date, String image, String invoiceNumber,String imageFile) {

    String requestIdField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + REQUEST_ID_KEY
            + "\";" + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String requestIdValue = requestId + "\r\n";
    String numberFiledField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + NUMBER_KEY + "\";"
            + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String invoiceValue = invoiceNumber + "\r\n";
    String dateField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + DATE_KEY + "\";"
            + "Content-type: " + CONTENT_TYPE + "\r\n value=\"12345\"" + "\r\n\r\n";
    String dateValue = date + "\r\n";
    String imageField = "--" + BOUNDARY + "\r\n Content-Disposition: form-data; name=\"" + IMAGE_KEY
            + "\"; filename=\"" + imageFile + "\"\r\n" + "Content-type: " + CONTENT_TYPE + "\r\n\r\n";
    String imageValue = image + "\r\n";
    String end = "\r\n--" + BOUNDARY + "--";
    return ArrayUtils.addAll((requestIdField + requestIdValue + numberFiledField + invoiceValue + dateField
            + dateValue + imageField + imageValue).getBytes(), ArrayUtils.addAll(data, end.getBytes()));
}
Shiau answered 17/8, 2018 at 9:4 Comment(0)
F
0

My way to test multipart/form-data via mock MVC sending params and files is the following:

    @Test
void testSendFeedback() throws Exception {
    var builder = MockMvcRequestBuilders.multipart(URL_PATH);

    Path path = Files.createTempFile("test-file", "tmp");
    builder = builder.part(new MockPart("image", path.toFile().getName(), Files.readAllBytes(path)));

    builder.param("field1", "value1")
        .param("fields2", "value2");

    mockMvc.perform(builder.header(HttpHeaders.AUTHORIZATION, YOUR_AUTH_VALUE).contentType(MediaType.MULTIPART_FORM_DATA_VALUE))
        .andDo(print())
        .andExpect(status().isNoContent());
}
Fluted answered 25/6, 2020 at 7:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.