Composing multipart/form-data with a different Content-Type on each parts with Javascript (or Angular)
Solution 1
According to the documentation of FormData
, you can append a field with a specific content type by using the Blob
constructor:
var formData = new FormData();
formData.append('items', new Blob([JSON.stringify({
name: "Book",
quantity: "12"
})], {
type: "application/json"
}));
After careful observation, it turns out that it will send the part as follows:
Content-Disposition: form-data; name="items"; filename="blob"
Content-Type: text/json
The only alternative, safe from building the whole request yourself is to pass a string value:
formData.append('items', '{"name": "Book", "quantity": "12"}');
This, unfortunately, doesn't set the Content-Type
header.
Solution 2
Mistake #1: I mistakenly assume that the items has to be a json, so that we can call its attribute.
Solution: To submit a multipart request that contain a file and an object like format is very simple.
form = new FormData();
form.append('items[name]', 'Book');
form.append('items[quantity]', 12);
form.append('image', imageFile);
form.append('owner', 'John Doe');
So thus the request header and body will looks something like this
POST /api/v1/inventory
Host: localhost:8000
Origin: http://localhost:9000
Content-Type: multipart/form-data; boundary=------border
------border
Content-Disposition: form-data; name="owner"
john doe
------border
Content-Disposition: form-data; name="image"; filename="mybook.png"
Content-Type: image/png
------border
Content-Disposition: form-data; name="items[name]"
Book
------border
Content-Disposition: form-data; name="items[quantity]"
12
------border--
Solution 3
Nothing would get this to work, until I set the Content-Type
header to undefined. In my case I am posting a file and some json.
public uploadFile(code: string, file):angular.IHttpPromise<any>{
var data = `{"query":"mutation FIRMSCORECARD_CALCULATE($code:String!){ FirmScorecardMutation{ BatchCalculate(Code:$code) }}","variables":{"code":"${code}"},"operationName":"FIRMSCORECARD_CALCULATE"}`;
var formData = new FormData();
formData.append('operations', data);
formData.append('file', file, file.name);
let config = {
headers: {
'Accept': 'application/json',
'Content-Type': undefined
}
};
let response = this.$http.post(this.graphqlUrl, formData, config);
return response;
}
Yeo
Abstraction Free is better then Open. Open is better than Close. Simple is better than Complex. Complex is better than Complicated Do one things and do it well, or don't do it at all. Always be friendly in communities so that you can help better in solving their problem. About Me I am Eugene. Worked in Autodesk. I am well rounded programmer, specialize in Python & JavaScript.
Updated on July 10, 2022Comments
-
Yeo almost 2 years
Wrong question asked, see my update below
I need to integrate my AngularJS Project with an existing RESTful API. These API consume POST request which
upload a file
, and also submit the form data in a request. Unfortunately, one of the form input requires to be inContent-Type: Application/json
.After search on the web, I could only
POST
withContent-Type: multipart/form-data
in which each of the parts does not have a specificMIME
. How can I compose mymultipart/form-data
with a differentMIME
for each parts in Javascript?POST /api/v1/inventory Host: localhost:8000 Origin: http://localhost:9000 Content-Type: multipart/form-data; boundary=------border ------border Content-Disposition: form-data; name="owner" john doe ------border Content-Disposition: form-data; name="image"; filename="mybook.png" Content-Type: image/png ------border Content-Disposition: form-data; name="items" Content-Type: application/json {"name": "Book", "quantity": "12"} ------border--
Relevant References:
- https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects
- REST - HTTP Post Multipart with JSON
- http://code.activestate.com/recipes/578846-composing-a-postable-http-request-with-multipartfo/
- application/x-www-form-urlencoded or multipart/form-data?
- https://stackoverflow.com/a/9082243/764592
Update
Apologize for asking a wrong question. The original problem is that, I can see the server calling the logic something like,
func POST(req): owner = req.owner // This is string image = req.image // This is file object itemQuantity = req.items.quantity // Items is an object with attribute quantity itemName = req.items.name // Items is an object with attribute name
I have also managed to figure out how to submit such a post request. I will post my answer below.
Once again sorry for asking a wrong question.
-
Yeo almost 10 yearsAlthough it set the
Content-Type: 'application/json'
, it is still wrapped under a Blob. Hence, the API reject my request. gist.github.com/9gix/… -
Ja͢ck almost 10 yearsAh, think I've made a typo; please try again.
-
Yeo almost 10 yearsThe result of my previous comment is based on the proper syntax of Blob.
-
Ja͢ck almost 10 years@Yeo Did you try
formData.append('items', JSON.stringify(...))
as well? -
Yeo almost 10 yearsbecause i'm using angular, therefore this is what I use to convert into JSON
angular.toJson()
Which I believe does the same things. docs.angularjs.org/api/ng/function/angular.toJson -
Ja͢ck almost 10 years@Yeo That should be the same; it seems that with FormData you either get to upload a file with a specific content type or just a string value (without specific content type).
-
iamserious over 9 yearsAh, you saved my day. I also assumed json had to be in a single object like a regular post data! thanks.
-
Isthar about 7 yearsThe fact of data being sent with
filename="blob"
is not a problem. This Blob wrapper is perfectly working for me, with Angular 2 as front end and Spring 4 as backend. -
cabaji99 about 6 yearsYeap works with blob: var blob = new Blob([JSON.stringify(requestVo)], { type: "application/json" }) formData.append('data',blob);
-
Shawn almost 3 yearsThank you! This was exactly what I needed!
-
José Mancharo over 2 yearsHow do I make this work in node.js, reading a file from the file-system? Blob is not available on node.