在 multipart/form-data POST 中缺少边界

我想使用 去找阿皮发送 new FormData()作为 POST请求的 body

手术过程是这样的:

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
}
)

这里的问题是边界

boundary=----WebKitFormBoundaryyEmKNDsBKjB7QEqu

从来没有进入 Content-Type:头部

它应该是这样的:

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

当你用 new XMLHttpRequest()尝试“相同”操作时,像这样:

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

标题设置正确

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

所以我的问题是:

  1. 在这种情况下,我如何使 fetch的行为与 XMLHttpRequest完全一样?
  2. 如果这是不可能的,为什么?

谢谢大家! 这个社区或多或少是我职业成功的原因。

121308 次浏览

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.

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.

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;

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}

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'


})

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
}
)

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