Parsing multipart/mixed responses with Node.js and axios - javascript

I've trying to parse a multipart/mixed response with (1) JSON and (2) .zip file.
I've been using Axios to GET the response, no problem there. I've seen npm packages like meros and multipart-mixed-parser but both just returned an empty array.
the response looks like
--Boundary_137_1895017408_1627074248456
Content-Type: application/json
{
"createdDate" : "2021-07-22T01:46:05.149+0000",
more JSON...
}
--Boundary_137_1895017408_1627074248456
Content-Type: application/octet-stream
Content-Disposition: form-data; filename="347b3bd1-e6e3-49a0-928d-9956df5c5af11671844264911435718.tmp"; modification-date="Fri, 23 Jul 2021 21:04:08 GMT"; size=620991; name="attachments.zip"
binary data here ...
--Boundary_137_1895017408_1627074248456--
async function eManGet (mtn, zipPath) {
try {
mtn = mtn.toUpperCase()
if (zipPath) {
const res = eManAPI.get({
url: `/emanifest/manifest/${mtn}/attachments`,
headers: {
Accept: 'multipart/mixed'
}
})
return res
} else {
...
}
// console.log(res.data)
} catch (error) {
...
}
}
The package I want to use this for is here
If you've faced this problem before, what package(s) did you use?

The dicer npm package does the job pretty well. You'll have to extract the boudary from headers yourself tho.

How to send multipart/mixed with axios
There is example of source code with request
const messages = body[endpoint].map(function (m) {
return {
'Content-Type': 'application/http',
body: 'GET ' + api + '/gmail/v1/users/me/' + endpoint + '/' + m.id + query + '\n'
}
})
const r = request({
method: 'POST',
url: api + '/batch/gmail/v1',
multipart: messages,
timeout: opts.timeout,
headers: {
'Authorization': 'Bearer ' + key,
'content-type': 'multipart/mixed'
}
})
from this library
https://github.com/SpiderStrategies/node-gmail-api
To solve this problem I used request catcher and wrote the following tests:
it('request catcher of request', () => {
const catcher = 'https://pl.requestcatcher.com/';
const messages = [1,2,3].map(function (index:number) {
return {
'Content-Type': 'application/http',
body: 'GET ' + `/gmail/v1/users/me/${index}\n`
}
})
const r = request({
method: 'POST',
url: catcher,
multipart: messages,
headers: {
'Authorization': 'Bearer KEY',
'content-type': 'multipart/mixed'
}
})
expect(r).toBeNull();
})
and saw the following request on catcher:
For axios there is not ready answer in all internet. This is my research:
You can use a FormData object in the browser. In Node.js the feature isn't supported yet by axios (see #789).
https://github.com/axios/axios/issues/803
[RFC2388] suggested that multiple files for a single form field be
transmitted using a nested "multipart/mixed" part. This usage is
deprecated.
https://datatracker.ietf.org/doc/html/rfc7578
info how to do it in request ( that is also depreciated )
https://github.com/request/request#multipartrelated
Function for generation boundary
https://github.com/form-data/form-data/blob/53adbd81e9bde27007b28083068f2fc8272614dc/lib/form_data.js#L347
So we can send multipart/mixed by this code
it('request catcher of axios', () => {
const catcher = 'https://pl.requestcatcher.com/';
const r = axios.post(catcher, `--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/1
--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/2
--3e50fc2b-7a5c-4565-99f2-1628010018be
Content-Type: application/http
GET /gmail/v1/users/me/3
--3e50fc2b-7a5c-4565-99f2-1628010018be--`,{
headers: {
'Authorization': 'Bearer KEY',
'content-type': 'multipart/mixed; boundary=3e50fc2b-7a5c-4565-99f2-1628010018be'
}
})
expect(r).not.toBeNull();
})
and now oly generation of boundary is problem to solve. Lets check differences between multipart/mixed and and multipart/form-data bounduaries.
This is exmple of bounduary from form-data package:
--------------------------942880057551350107765038
and this is from mixed (from request):
--3e50fc2b-7a5c-4565-99f2-1628010018be
and this is proper RFC
https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
so we can use this code
> `--${crypto.randomUUID()}`
'--7784084f-e129-44ea-aa64-8f480a26c75f'
And this is code that sends mutipart/mixed by axios
it('request catcher of axios', () => {
const catcher = 'https://pl.requestcatcher.com/';
interface SingleMultipartMixedMessage {
'Content-Type': string,
body: string
}
const messages = [1, 2, 3].map(function (index: number) {
return {
'Content-Type': 'application/http',
body: 'GET ' + `/gmail/v1/users/me/${index}\n`
}
})
const boundary = `--${crypto.randomUUID()}`
const body = messages.reduce((p: string, n: SingleMultipartMixedMessage) => {
return p + `\nContent-Type: ${n['Content-Type']}\n\n${n.body}\n${boundary}`
}, boundary)
const r = axios.post(catcher, body, {
headers: {
'Authorization': 'Bearer KEY',
'content-type': `multipart/mixed; boundary=${boundary.replace(/^--/, '')}`
}
})
expect(r).not.toBeNull();
})
and comparison of requests caught by caught between reference implementation [ request ] and our code [ axios ]
How to parse multipart mixed
Get boundary from content-type header
Add -- before it
Split body by this boundary
Any part split by double \n
Parse any pair of headers and data individually

Related

How To Solve "Expected type object but found type string" in REST API calls? (reactjs, javascript)

I'm trying to use following Auth0 API call: https://auth0.com/docs/api/management/v2#!/Users/patch_users_by_id
const sUserMetadata = async () => {
const domain = "xxxxxxxxxxxxxxx"
try {
const accessToken = await getAccessTokenSilently({
audience: `${domain}/api/v2/`,
scope: "update:current_user_metadata",
});
const userDetailsByIdUrl = `${domain}/api/v2/users/${user.sub}`;
const metadataResponse = await fetch(userDetailsByIdUrl, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: { "email_verified": true }
})
let user_metadata = await metadataResponse;
console.log(user_metadata)
} catch (e) {
console.log(e.message);
}
};
sUserMetadata().then(r => null);
I am receiving following response error:
{"statusCode":400,"error":"Bad Request","message":"Payload validation error: 'Expected type object but found type string'.","errorCode":"invalid_body"}
Obviously the Body-Tag provides it in the correct form with Bracets {} so it Should! be an Object.
I have tried:
JSON.parse()
I have tried to add Content-Type which results in a freaking "SYNTAX ERROR" because of the - in content-type which doesnt make any sense because under chrome debugger I can obviously see that there is a property called content-type: text/plain;charset=UTF-8 and I have no idea how else I am supposed to change this?
headers: {
Authorization: `Bearer ${accessToken}`,
Content-Type: 'application/json',
},
Putting Content-Type inside Apostrophes 'Content-Type' so it doesn't give a Syntax Error and then you using JSON.Stringify() at the Body-Tag part fixes the problem.
const metadataResponse = await fetch(userDetailsByIdUrl, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ "user_metadata" : { "addresses": {"work_address": "100 Industrial Way"} }}),
})
PS: save me from javascript pls

POST https://accounts.spotify.com/api/token 415 error when using fetch

I'm trying to make a request to get an authorization code from the spotify api using fetch but I keep getting a 415 error code. I did not have any errors when i was originally using $.ajax instead of fetch.
let client_id = '8f10fa8af1aa40c6b52073811460bf33'
let client_secret = '27a7c01912444b409a7f9a6d1f700868'
let ah = btoa(client_id + ":" + client_secret)
const getAuthToken = (searchedTerm) => {
fetch( `https://accounts.spotify.com/api/token`,
{
headers: {
'Content-Type': 'application/x-www-form-url-encoded',
'Authorization': `Basic ${ah}`
},
body: {
grant_type: 'client_credentials'
},
json: true,
method : "POST"
}
)
.then(function(response) {
authToken = response.access_token;
spotifySearch(response.access_token, searchedTerm);
})
}
See this answer on a similar post. Note that there they set 'Content-Type':'application/x-www-form-urlencoded', with no hyphen between url and encoded. I think you simply need to change
headers: {
'Content-Type': 'application/x-www-form-url-encoded',
'Authorization': `Basic ${ah}`
},
to
headers: {
'Content-Type': 'application/x-www-form-urlencoded', // no hyphen in urlencoded
'Authorization': `Basic ${ah}`
},

How to post file data to Gitlab project using JavaScript fetch [duplicate]

I'm trying to POST a JSON object using fetch.
From what I can understand, I need to attach a stringified object to the body of the request, e.g.:
fetch("/echo/json/",
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify({a: 1, b: 2})
})
.then(function(res){ console.log(res) })
.catch(function(res){ console.log(res) })
When using jsfiddle's JSON echo I'd expect to see the object I've sent ({a: 1, b: 2}) back, but this does not happen - chrome devtools doesn't even show the JSON as part of the request, which means that it's not being sent.
With ES2017 async/await support, this is how to POST a JSON payload:
(async () => {
const rawResponse = await fetch('https://httpbin.org/post', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 1, b: 'Textual content'})
});
const content = await rawResponse.json();
console.log(content);
})();
Can't use ES2017? See #vp_art's answer using promises
The question however is asking for an issue caused by a long since fixed chrome bug.
Original answer follows.
chrome devtools doesn't even show the JSON as part of the request
This is the real issue here, and it's a bug with chrome devtools, fixed in Chrome 46.
That code works fine - it is POSTing the JSON correctly, it just cannot be seen.
I'd expect to see the object I've sent back
that's not working because that is not the correct format for JSfiddle's echo.
The correct code is:
var payload = {
a: 1,
b: 2
};
var data = new FormData();
data.append( "json", JSON.stringify( payload ) );
fetch("/echo/json/",
{
method: "POST",
body: data
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })
For endpoints accepting JSON payloads, the original code is correct
I think your issue is jsfiddle can process form-urlencoded request only. But correct way to make json request is pass correct json as a body:
fetch('https://httpbin.org/post', {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({a: 7, str: 'Some string: &=&'})
}).then(res => res.json())
.then(res => console.log(res));
From search engines, I ended up on this topic for non-json posting data with fetch, so thought I would add this.
For non-json you don't have to use form data. You can simply set the Content-Type header to application/x-www-form-urlencoded and use a string:
fetch('url here', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
body: 'foo=bar&blah=1'
});
An alternative way to build that body string, rather then typing it out as I did above, is to use libraries. For instance the stringify function from query-string or qs packages. So using this it would look like:
import queryString from 'query-string'; // import the queryString class
fetch('url here', {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
body: queryString.stringify({for:'bar', blah:1}) //use the stringify object of the queryString class
});
After spending some times, reverse engineering jsFiddle, trying to generate payload - there is an effect.
Please take eye (care) on line return response.json(); where response is not a response - it is promise.
var json = {
json: JSON.stringify({
a: 1,
b: 2
}),
delay: 3
};
fetch('/echo/json/', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then(function (response) {
return response.json();
})
.then(function (result) {
alert(result);
})
.catch (function (error) {
console.log('Request failed', error);
});
jsFiddle: http://jsfiddle.net/egxt6cpz/46/ && Firefox > 39 && Chrome > 42
2021 answer: just in case you land here looking for how to make GET and POST Fetch api requests using async/await or promises as compared to axios.
I'm using jsonplaceholder fake API to demonstrate:
Fetch api GET request using async/await:
const asyncGetCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncGetCall()
Fetch api POST request using async/await:
const asyncPostCall = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
});
const data = await response.json();
// enter you logic when the fetch is successful
console.log(data);
} catch(error) {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
}
}
asyncPostCall()
GET request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
POST request using Promises:
fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
})
.then(res => res.json())
.then(data => {
// enter you logic when the fetch is successful
console.log(data)
})
.catch(error => {
// enter your logic for when there is an error (ex. error toast)
console.log(error)
})
GET request using Axios:
const axiosGetCall = async () => {
try {
const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosGetCall()
POST request using Axios:
const axiosPostCall = async () => {
try {
const { data } = await axios.post('https://jsonplaceholder.typicode.com/posts', {
// your expected POST request payload goes here
title: "My post title",
body: "My post content."
})
// enter you logic when the fetch is successful
console.log(`data: `, data)
} catch (error) {
// enter your logic for when there is an error (ex. error toast)
console.log(`error: `, error)
}
}
axiosPostCall()
I have created a thin wrapper around fetch() with many improvements if you are using a purely json REST API:
// Small library to improve on fetch() usage
const api = function(method, url, data, headers = {}){
return fetch(url, {
method: method.toUpperCase(),
body: JSON.stringify(data), // send it as stringified json
credentials: api.credentials, // to keep the session on the request
headers: Object.assign({}, api.headers, headers) // extend the headers
}).then(res => res.ok ? res.json() : Promise.reject(res));
};
// Defaults that can be globally overwritten
api.credentials = 'include';
api.headers = {
'csrf-token': window.csrf || '', // only if globally set, otherwise ignored
'Accept': 'application/json', // receive json
'Content-Type': 'application/json' // send json
};
// Convenient methods
['get', 'post', 'put', 'delete'].forEach(method => {
api[method] = api.bind(null, method);
});
To use it you have the variable api and 4 methods:
api.get('/todo').then(all => { /* ... */ });
And within an async function:
const all = await api.get('/todo');
// ...
Example with jQuery:
$('.like').on('click', async e => {
const id = 123; // Get it however it is better suited
await api.put(`/like/${id}`, { like: true });
// Whatever:
$(e.target).addClass('active dislike').removeClass('like');
});
Had the same issue - no body was sent from a client to a server.
Adding Content-Type header solved it for me:
var headers = new Headers();
headers.append('Accept', 'application/json'); // This one is enough for GET requests
headers.append('Content-Type', 'application/json'); // This one sends body
return fetch('/some/endpoint', {
method: 'POST',
mode: 'same-origin',
credentials: 'include',
redirect: 'follow',
headers: headers,
body: JSON.stringify({
name: 'John',
surname: 'Doe'
}),
}).then(resp => {
...
}).catch(err => {
...
})
This is related to Content-Type. As you might have noticed from other discussions and answers to this question some people were able to solve it by setting Content-Type: 'application/json'. Unfortunately in my case it didn't work, my POST request was still empty on the server side.
However, if you try with jQuery's $.post() and it's working, the reason is probably because of jQuery using Content-Type: 'x-www-form-urlencoded' instead of application/json.
data = Object.keys(data).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])).join('&')
fetch('/api/', {
method: 'post',
credentials: "include",
body: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
The top answer doesn't work for PHP7, because it has wrong encoding, but I could figure the right encoding out with the other answers. This code also sends authentication cookies, which you probably want when dealing with e.g. PHP forums:
julia = function(juliacode) {
fetch('julia.php', {
method: "POST",
credentials: "include", // send cookies
headers: {
'Accept': 'application/json, text/plain, */*',
//'Content-Type': 'application/json'
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
},
body: "juliacode=" + encodeURIComponent(juliacode)
})
.then(function(response) {
return response.json(); // .text();
})
.then(function(myJson) {
console.log(myJson);
});
}
It might be useful to somebody:
I was having the issue that formdata was not being sent for my request
In my case it was a combination of following headers that were also causing the issue and the wrong Content-Type.
So I was sending these two headers with the request and it wasn't sending the formdata when I removed the headers that worked.
"X-Prototype-Version" : "1.6.1",
"X-Requested-With" : "XMLHttpRequest"
Also as other answers suggest that the Content-Type header needs to be correct.
For my request the correct Content-Type header was:
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
So bottom line if your formdata is not being attached to the Request then it could potentially be your headers. Try bringing your headers to a minimum and then try adding them one by one to see if your problem is resolved.
If your JSON payload contains arrays and nested objects, I would use URLSearchParams and jQuery's param() method.
fetch('/somewhere', {
method: 'POST',
body: new URLSearchParams($.param(payload))
})
To your server, this will look like a standard HTML <form> being POSTed.
You could do it even better with await/async.
The parameters of http request:
const _url = 'https://jsonplaceholder.typicode.com/posts';
let _body = JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1,
});
const _headers = {
'Content-type': 'application/json; charset=UTF-8',
};
const _options = { method: 'POST', headers: _headers, body: _body };
With clean async/await syntax:
const response = await fetch(_url, _options);
if (response.status >= 200 && response.status <= 204) {
let data = await response.json();
console.log(data);
} else {
console.log(`something wrong, the server code: ${response.status}`);
}
With old fashion fetch().then().then():
fetch(_url, _options)
.then((res) => res.json())
.then((json) => console.log(json));
**//POST a request**
const createTodo = async (todo) => {
let options = {
method: "POST",
headers: {
"Content-Type":"application/json",
},
body: JSON.stringify(todo)
}
let p = await fetch("https://jsonplaceholder.typicode.com/posts", options);
let response = await p.json();
return response;
}
**//GET request**
const getTodo = async (id) => {
let response = await fetch('https://jsonplaceholder.typicode.com/posts/' + id);
let r = await response.json();
return r;
}
const mainFunc = async () => {
let todo = {
title: "milan7",
body: "dai7",
userID: 101
}
let todor = await createTodo(todo);
console.log(todor);
console.log(await getTodo(5));
}
mainFunc()
I think that, we don't need parse the JSON object into a string, if the remote server accepts json into they request, just run:
const request = await fetch ('/echo/json', {
headers: {
'Content-type': 'application/json'
},
method: 'POST',
body: { a: 1, b: 2 }
});
Such as the curl request
curl -v -X POST -H 'Content-Type: application/json' -d '#data.json' '/echo/json'
In case to the remote serve not accept a json file as the body, just send a dataForm:
const data = new FormData ();
data.append ('a', 1);
data.append ('b', 2);
const request = await fetch ('/echo/form', {
headers: {
'Content-type': 'application/x-www-form-urlencoded'
},
method: 'POST',
body: data
});
Such as the curl request
curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '#data.txt' '/echo/form'
You only need to check if response is ok coz the call not returning anything.
var json = {
json: JSON.stringify({
a: 1,
b: 2
}),
delay: 3
};
fetch('/echo/json/', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then((response) => {if(response.ok){alert("the call works ok")}})
.catch (function (error) {
console.log('Request failed', error);
});
// extend FormData for direct use of js objects
Object.defineProperties(FormData.prototype, {
load: {
value: function (d) {
for (var v in d) {
this.append(v, typeof d[v] === 'string' ? d[v] : JSON.stringify(d[v]));
}
}
}
})
var F = new FormData;
F.load({A:1,B:2});
fetch('url_target?C=3&D=blabla', {
method: "POST",
body: F
}).then( response_handler )
you can use fill-fetch, which is an extension of fetch. Simply, you can post data as below:
import { fill } from 'fill-fetch';
const fetcher = fill();
fetcher.config.timeout = 3000;
fetcher.config.maxConcurrence = 10;
fetcher.config.baseURL = 'http://www.github.com';
const res = await fetcher.post('/', { a: 1 }, {
headers: {
'bearer': '1234'
}
});

React native 422 Unprocessable Entity error returned from server when using POST Fetch call with content type: application/x-www-form-urlencoded

I'm calling a simple login API with POST request following are the params:
Headers:
Content-type: application/x-www-form-urlencoded
Body:
email: String
password
Error returned from server is:422 Unprocessable Entity
CODE:
var formBody = new FormData();
formBody.set("email", "test5#gmail.com");
formBody.set("password", "12345678");
const data = new URLSearchParams(new FormData(details));
return dispatch => {
dispatch(requestData());
try {
fetch(`${BASE_URL}users/sign_in`, {
method: 'POST',
// headers: Interceptor.getHeaders(),
headers: {
Accept:'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
// body: formBody
body: data
})
.then(res => res.json())
.then(result=>
{
if (result.success === false) {}
}
)
} catch (error) {
console.log('error',error)
dispatch(failureData(error))
}
}
Screenshot of code
Got the answer, 422 is basically caused by semantic issue, in my case, Origin of my Request Header was going null.

Node request module - trouble sending formdata

I'm trying to send a post request from my node server to an api that expects a file and other form data as an multipart/form-data.
Here is what my code looks like
var importResponse = function(csv){
stringify(csv, function(err, output){
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
formData: {
surveyId: surveyId,
file: {
value: output,
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
}
}
}, function(error, response, body){
console.log(body);
});
});
}
Using request-debug here is the request:
request:
{ debugId: 1,
uri: 'https://co1.qualtrics.com/API/v3/responseimports',
method: 'POST',
headers:
{ 'X-API-TOKEN': 'removed',
'content-type':
'multipart/form-data; boundary=--------------------------010815605562947295265820',
host: 'co1.qualtrics.com',
'content-length': 575 } } }
and the response:
response:
{ debugId: 1,
headers:
{ 'content-type': 'application/json',
'content-length': '188',
'x-edgeconnect-midmile-rtt': '28',
'x-edgeconnect-origin-mex-latency': '56',
date: 'Wed, 18 Jul 2018 03:57:59 GMT',
connection: 'close',
'set-cookie': [Array],
'strict-transport-security': 'max-age=31536000; includeSubDomains; preload' },
statusCode: 400,
body:
'{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing Content-Type for file part. name=file","errorCode":"MFDP_3"},"requestId":"322a16db-97f4-49e5-bf10-2ecd7665972e"}}' } }
The error I'm getting is: Missing Content-Type for file part.
I've added this in the options:
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
When I look at the request, it seems as though the form data isn't included. But perhaps that is just the request-debug not showing it.
I saw a similar SO question and the answer was to use JSON.stringify.
I tried changing my code to the following:
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
body: JSON.stringify({
surveyId: surveyId,
file: {
value: output,
options: {
fileName: 'test.csv',
contentType:'text/csv'
}
}
})
However, I got the following error:
{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing boundary header"}}}
What am I doing wrong?
UPDATE
When I tried changing the file value to a csv on my computer fs.createReadStream('test.csv'), it worked fine
file: {
value: fs.createReadStream('test.csv'),
options: {
contentType: 'text/csv'
}
}
So I assume there is something wrong with the way I'm giving the file. The output variable that I'm using as the file just looks like "QID1,QID2\nQID1,QID2\n1,2". I assume this is causing the problems, even though the error is a bit misleading. I tried creating a Readable that I found as a StackOverFlow answer like so:
var s = new Readable
s.push(output)
s.push(null)
However, this lead to a Unexpected end of input
{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Unexpected end of input"}}}
I found the issue. My first solution was fine, but instead of fileName it should have been filename
var importResponse = function(csv){
stringify(csv, function(err, output){
request.post({
headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
url: url,
formData: {
surveyId: surveyId,
file: {
value: output,
options: {
filename: 'test.csv', //filename NOT fileName
contentType:'text/csv'
}
}
}
}, function(error, response, body){
console.log(body);
});
});
}
Is it possible that you are using the incorrect property name for your file?
A quick read of the forms info for the request Node module makes me think you should be using custom_file instead of file.
You can read more about it here: https://github.com/request/request#forms
Hey Eric check in which format they accept multipartRequest ,as I did for uploading the file on drive like this:
request(options, function (err, response) {
var boundary = '-------314159265358979323846';
var delimiter = "\r\n--" + boundary + "\r\n";
var close_delim = "\r\n--" + boundary + "--";
var fileContent = 'Sample upload :)';
var metadata = {
'name': 'myFile.txt',
'mimeType': 'text/plain\r\n\r\n'
};
var multipartRequestBody = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: ' + 'text/plain\r\n\r\n' + fileContent + close_delim;
request(options, function (err, response) {
var url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&access_token=' + JSON.parse(response.body).access_token;
var options = {
method: 'POST',
url: url,
headers: {
'Content-Type': 'multipart/related; boundary="' + boundary + '"'
},
body: multipartRequestBody
};
request(options, function (err, response) {
res.send({resultdata: response.body});
});
});
});
Set the multi-part as per your endpoint accepting.
Using frameworks like express(they automatically parse headers and response) and npm modules like multer for handling multipart form data helps cause they do all the heavy lifting for you

Categories