116

I want to send a new FormData() as the body of a POST request using the fetch api

The operation looks something like this:

var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')

fetch('https://api.myapp.com', 
  {
    method: 'POST',
    headers: {
      "Content-Type": "multipart/form-data"
    },
    body: formData
  }
)

The problem here is that the boundary, something like

boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu

never makes it into the Content-Type: header

It should look like this:

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

When you try the "same" operation with a new XMLHttpRequest(), like so:

var request = new XMLHttpRequest()
request.open("POST", "https://api.mything.com")
request.withCredentials = true
request.send(formData)

the headers are correctly set

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

So my questions are:

  1. how do I make fetch behave exactly like XMLHttpRequest in this situation?
  2. if this is not possible, why?

Thanks everybody! This community is more or less the reason I have professional success.

Zeitounator
  • 38,476
  • 7
  • 53
  • 66
James
  • 4,927
  • 3
  • 22
  • 27

7 Answers7

210

The solution to the problem is to explicitly set Content-Type to undefined so that your browser or whatever client you're using can set it and add that boundary value in there for you. Disappointing but true.

James
  • 4,927
  • 3
  • 22
  • 27
  • 33
    using fetch, I removed the Content-Type header and it worked. – sww314 Feb 28 '17 at 16:02
  • 27
    Unbelievable!! I spent hours until I found this!! `delete result.headers['Content-Type'];` worked for me, thanks!! – Crysfel Apr 12 '17 at 01:26
  • 3
    @Crysfel Thanks.. deleting content type worked for me :P – master_dodo Jun 09 '17 at 16:28
  • 11
    doesn't work for me. Setting to undefined defaults to text/plain. – sebnukem Jul 05 '17 at 01:51
  • @sebnukem which browser? I've never seen that happen, I'll admit though, I switched over to using Axios, it has a nicer api / behavior imo – James Jul 07 '17 at 05:41
  • @James No problem with the browser. I found out that the HTTP library I was using coerced the body payload to a string. – sebnukem Jul 07 '17 at 12:51
  • @sebnukem which http lib was that? this post is about `fetch` – James Jul 07 '17 at 17:31
  • @James, an in-house, proprietary lib – sebnukem Jul 07 '17 at 17:55
  • But why is it so? – Green Sep 08 '17 at 05:29
  • @Green Apparently you can define an arbitrary boundary -> https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data -- I just didn't, I only set the content type, but that is only half the job. On the other hand, If you just leave the content type alone, the browser can infer it, and set the type + the boundary to an appropriate compliant, random string for you. – James Sep 09 '17 at 18:08
  • 6
    Neither setting the content-type to undefined, nor deleting the content-type before making the POST with fetch did it for me... – Ahab Jan 16 '18 at 13:11
  • 2 years later, I'd recommend using something like axios unless you absolutely need isomorphic fetch - over the years I've found fetch to be more of a nuisance than a help – James Jan 16 '18 at 14:54
  • 1
    Setting value to undefined didn't work for me, but removing header worked :) – Oscar Calderon Mar 09 '18 at 15:13
  • explicitly setting `Content-Type` to `undefined` worked for me – jaeyow Mar 28 '18 at 05:38
  • 1
    Setting the Content-Type header to undefined seemed to come across for me as "Content-Type": undefined in the request itself. not defining it worked. – Scribblemacher Dec 23 '20 at 19:57
  • 1
    JFC it took me way too long to find this solution. Setting it to undefined worked. – geeberry Jan 22 '23 at 19:42
  • I will never get 2 hours of my life back because of this. – Eamonn Gahan Jul 31 '23 at 23:05
  • This saved me. Thank you! – tmarois Aug 19 '23 at 17:28
19

I had the same issue, and was able to fix it by excluding the Content-Type property, allowing the browser to detect and set the boundary and content type automatically.

Your code becomes:

var formData = new FormData()
formData.append('myfile', file, 'someFileName.csv')

fetch('https://api.myapp.com',
  {
    method: 'POST',
    body: formData
  }
)
Andrew Faulkner
  • 3,662
  • 3
  • 21
  • 24
17

I removed "Content-Type" and added 'Accept' to http headers and it worked for me. Here are the headers I used,

'headers': new HttpHeaders({
        // 'Content-Type': undefined,
        'Accept': '*/*',
        'Authorization': 
        "Bearer "+(JSON.parse(sessionStorage.getItem('token')).token),
        'Access-Control-Allow-Origin': this.apiURL,
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, PATCH, DELETE',
        'Access-Control-Allow-Headers': 'origin,X-Requested-With,content-type,accept',
        'Access-Control-Allow-Credentials': 'true' 

      })
15
fetch(url,options)
  1. If you set a string as options.body, you have to set the Content-Type in request header ,or it will be text/plain by default.
  2. If options.body is specific object like let a = new FormData() or let b = new URLSearchParams(), you don't have to set the Content-Type by hand.It will be added automaticlly.

    • for a ,it will be something like

    multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

    as you see, the boundary is automaticlly added.

    • for b, it is application/x-www-form-urlencoded;
moonreader
  • 169
  • 1
  • 5
7

According to FormData documentation, you shoudn't manually set the Content-Type header so browser itself will set it correctly:

Warning: When using FormData to submit POST requests using XMLHttpRequest or the Fetch_API with the multipart/form-data Content-Type (e.g. when uploading Files and Blobs to the server), do not explicitly set the Content-Type header on the request. Doing so will prevent the browser from being able to set the Content-Type header with the boundary expression it will use to delimit form fields in the request body.

So if your code (or library/middleware/etc) manually set the Content-Type, you have two ways to fix it:

  1. rewrote your code (or whatever you use) to don't set Content-Type by default
  2. set Content-Type to undefined or remove it from headers to let your browser do it's work
6

Add headers:{content-type: undefined} browser will generate a boundary for you that is for uploading a file part-and-part with streaming if you are adding 'multiple/form-data' it means you should create streaming and upload your file part-and-part

So it is okay to add request.headers = {content-type: undefined}

Nver Abgaryan
  • 621
  • 8
  • 13
4

I'm using the aurelia-api (an wrapper to aurelia-fetch-client). In this case the Content-Type default is 'application/json'. So I set the Content-Type to undefined and it worked like a charm.

Diego Troitiño
  • 301
  • 2
  • 6