preface

There was a weird Bug in the POST request that sent FormData. The browser automatically set the content-type to Application/JSON, causing the backend to fail to parse the correct data. While trying to solve this problem, I have studied multipart/form-data data in depth, so I have written this article to share it with you.

Multipart/form – the introduction of the data

In general, the front-end sends different types of data according to the scenario when making a Post request. The Content-Type field in the Post request header must declare the corresponding data Type. For example, application/ X-www-form-urlencoded, Application/JSON, multipart/form-data, etc., will be set manually when requested according to practical applications. In most cases, however, the browser will set the request header based on the data coming from the Body.

FormData is set to multipart/form-data when a POST request is sent:

Content-Type: multipart/form-data; boundary=xxxx

Copy the code

The first part represents the data type, while boundary represents the delimiter. The corresponding XXXX of boundary is set by the requester.

Usually we use the FromData format when we need to send a file or when we need to send a file with text. The FromData format is also represented by key-value pairs, but the actual message format is as follows

xxxx

Content-Disposition: form-data; name="foo"



bar

xxxx

Content-Disposition: form-data; name="baz"



The first line.

The second line.



xxxx

Copy the code

This FormData type can be understood as having two keys, foo and baz, separated by boundary. Here is XXXX.

Request form

In general, form submission is one of the most common sending methods in the past. It can be sent by setting encType in the

tag to multipart/form-data. We often use this format when submitting forms with files.

<form action="example.com" method="post" enctype="multipart/form-data">

  <fieldset>

    <legend>Registration example</legend>

    <p>

      First name: <input type="text" name="firstname" /><br />

      Last name: <input type="text" name="lastname" />

      <input type="file" name="file" />

    </p>

    <p>

      <input type="submit" />

    </p>

  </fieldset>

</form>

Copy the code

In this form submission scenario, we don’t need to manually set the Content-Type, so we don’t have to worry about how the data is arranged in the Body, we don’t need to know what boundary is, it’s all done automatically by the browser.

Use Ajax and FormData

Modern front-end development rarely uses pure form submission. Forms are usually used to fetch data, such as to fetch file data and then instantiate an object of type FormData to store that data. The HTTP request is then made via Ajax or FETCH.

const file = document.querySelector('#file') [0]

const formData = new FormData()



formData.append('file', file)

formData.append('firstName'.'Harlan')

formData.append('LastName'.'Zhang')



http.post('example.com', formData)

Copy the code

In particular, we don’t need to set the Content-Type, because the browser does it automatically. If we manually set the Content-Type, it overwrites the browser’s own Settings, and since we don’t know what the boundary delimiter is inside the formData object, Therefore, after receiving the data, the backend cannot find the boundary in the Content-Type, or the value of boundary is inconsistent with that in the formData, resulting in the failure to obtain the correct data.

Do not use the FormData type for Ajax requests

This brings up the Bug I mentioned at the beginning, the H5 page developed by the company itself, which occurs in the built-in browser of Dingding for IOS. The browser automatically sets the Content-type of Post requests that send FormData data to Application/JSON, causing the backend to fail to parse the correct data. Here I tried to set the request header manually, but as mentioned above, I couldn’t get the boundary in the formData object by any means. Later, I decided to use string directly to simulate the real format of storing data of formData instead of formData in the Body, as follows:

xxxx

Content-Disposition: form-data; name="foo"



bar

xxxx

Content-Disposition: form-data; name="baz"



The first line.

The second line.



xxxx



Copy the code

Simulate this type of string and send it inside the body. Boundary can also define itself.

One of the trickier parts is how to convert the data of type File. In this case, you need to use FileReader to read the contents of type File, which is type Buffer, and then convert it into a String that emulates FormData.

Finally, put the string that simulates FormData into the Body and issue it using a Post request. At this point we can manually set the Content-Type for the Post request, and we already know the value of boundary.

As for the specific implementation, we can refer to the EXAMPLE on MDN, here is more complete source code.

conclusion

To be honest, it is more difficult to simulate FormData than expected. The difficulty is mainly in the processing of File data. If the data is large, it must be fragmented to install and replace, otherwise it will exceed the storage limit of variables, which is a bit like uploading large File fragments. But also encountered PNG format file conversion after the back end is still unable to identify the phenomenon. The purpose of this article is to document and share some of the details of Post requests of the multipart/form-data type.