fetch - Missing boundary in multipart/form-data POST
Asked Answered
G

8

132

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.

Granitite answered 1/9, 2016 at 20:33 Comment(0)
G
236

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.

Granitite answered 1/9, 2016 at 21:30 Comment(15)
using fetch, I removed the Content-Type header and it worked.Azine
Unbelievable!! I spent hours until I found this!! delete result.headers['Content-Type']; worked for me, thanks!!Mukul
doesn't work for me. Setting to undefined defaults to text/plain.Tedra
@Tedra which browser? I've never seen that happen, I'll admit though, I switched over to using Axios, it has a nicer api / behavior imoGranitite
@Granitite No problem with the browser. I found out that the HTTP library I was using coerced the body payload to a string.Tedra
@Tedra which http lib was that? this post is about fetchGranitite
@James, an in-house, proprietary libTedra
But why is it so?Chaldea
@Chaldea Apparently you can define an arbitrary boundary -> #3508838 -- 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.Granitite
Neither setting the content-type to undefined, nor deleting the content-type before making the POST with fetch did it for me...Consciencestricken
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 helpGranitite
Setting value to undefined didn't work for me, but removing header worked :)Wakeful
explicitly setting Content-Type to undefined worked for meEmery
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.Cheerful
@Granitite thx man.. you save my live.. I was suffering and looking for a solution to this problem two days ago.. thx again!!Nutcracker
D
24

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
  }
)
Dispose answered 9/3, 2019 at 2:27 Comment(0)
O
20

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' 

      })
Overlive answered 19/12, 2018 at 7:16 Comment(2)
No other solutions worked for me. After adding Accept: / header, everything worked like a charm.Bewick
This was exactly it - for Angular 15, leaving out the Content-Type just got it set somewhere else in Angular as a default undefined, however, explicitly only providing the Accept header excluded the Content-Type header, and now I'm home free! Great solution!Musca
A
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;
Agro answered 23/8, 2017 at 12:56 Comment(2)
Very well that you told us that boundary is added automatically. We have all known that before.Chaldea
your explanation makes much more sense, thank you! I managed to get my code working!Radiotherapy
H
13

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. rewrite 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
Humid answered 18/7, 2022 at 14:30 Comment(0)
G
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}

Guayule answered 4/10, 2018 at 13:4 Comment(0)
N
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.

Noctilucent answered 12/1, 2017 at 2:32 Comment(0)
L
0

I just use axios and it fixed without any special parameters. the node fetch really sucks

Libya answered 26/2, 2024 at 21:5 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.