status code 400 with no error object when uploading to box - node.js

I'm trying to use the request.jslibrary to upload a file to box.com using request.post.
I consistently get a return code of 400 and a null body in the response. Not sure how to get at the actual error that box is seeing. The err argument to the callback is null, so there is a response from box.com, but with a statuscode of 400 and a null body.
FYI, the upload succeeds using curl, so the auth token etc. is fine.
I pointed the function below to http://echo.200please.com', and it seems the HTTP POST request I'm sending out is fine.
How do I get to see what error is being seen by box?
request = require"request");
UploadFile = function(filename, callback) {
var formData = {
attributes: JSON.stringify( {
name: filename,
parent: { id: '' + 2764913765 }
}),
file: fs.createReadStream('./temp.bin')
}
var options = {
url: 'https://upload.box.com/api/2.0/files.content',
headers: { Authorization: 'Bearer ' + tokens.access_token},
formData: formData
}
request.post(options,
function(err, response, body) {
if (err) {
console.log('Error Uploading the file');
} else {
console.log('returned:' + body + JSON.stringify(response.headers))
}
});
If I change the URL to point to echo.200please.com, the response I get from echo.200please.com is below, which seems to be the correct format for a file upload request.
> POST / HTTP/1.0
Host: echo.200please.com
Connection: close
Content-Length: 1951
Authorization: Bearer bVPDzG8PgIVRNoqb5LOzD61h6NXhJ6h0
content-type: multipart/form-data; boundary=--------------------------799592280904953105406767
----------------------------799592280904953105406767
Content-Disposition: form-data; name="attributes"
{"name":"testchunkname.1","parent":{"id":"2764913765"}}
----------------------------799592280904953105406767
Content-Disposition: form-data; name="file"; filename="temp.bin"
Content-Type: application/octet-stream
<... file data ...>

OK ... I found the bug :-)
It's a typo in the url
in my program it got set to api/2.0/files.content whereas the correct path in the url should be api/2.0/files/content

Related

Javascript File object missing from body

I have a valid object file with data in it when I do the request on the front end.
But when I receive the body on the express backend the file object is empty.
let body = {
metadata: {
date: date,
location: location,
description: description,
file: file
}
}
const headers = {'body': JSON.stringify(body)}
axios.get('/api/xummMint', {headers}).then( (res) => {
console.log("xumm data coming");
console.log(res)
setData({xummData: res});
})
when I console.log(req.headers.body) I receive an empty file object even though the file had data on the frontend:
{"metadata":{"date":"2022-12-30","location":"asf","description":"asdf","file":{}}}
I tried using fetch and adding content-Type: application/json but still same results. Can anyone help?
Chances are that you meant to send this as a POST request with a JSON body, not as a GET request with a header called "body"
axios.post(
'/api/xummMint',
body,
{headers: {'Content-Type': 'application/json'}}
).then(res => {
console.log("xumm data coming");
console.log(res);
setData({xummData: res});
});

Node.js REST webservice with wrong Content-Type

I'm developing a REST webservice in Node.js, with fastify framework, designed to respond to a specific client.
This is how the client calls my webservice:
POST /myws/scan HTTP/1.1
Host: myhost.io
User-Agent: AGENT
Content-Length: 45
Accept: */*
Content-Type: application/x-www-form-urlencoded
X-Forwarded-For: xxx.xxx.xxx.xxx
X-Forwarded-Proto: http
Accept-Encoding: gzip
{"FIELD1":"testvalue","FIELD2":True,"FIELD3":90}
As you can see the Content-Type is "application/x-www-form-urlencoded" but the request payload is in "application/json" format.
I can't change the client, so I have to adapt my webservice to manage this kind of calls.
I tried with #fastify/formbody package but I receive an error because I think it expects the request body in "application/x-www-form-urlencoded" format.
I tried also with this code:
app.addContentTypeParser('application/x-www-form-urlencoded', function (req, body, done) {
try {
var json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
but I have a JSON deserializing error.
How I can manage these kind of calls and which is the best way to do that?
body is a stream.
You need to add the parseAs option:
fastify.addContentTypeParser(
'application/x-www-form-urlencoded',
{ parseAs: 'string' },
function (req, body, done) {
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
}
)
I would check the first char before running the JSON.parse and I would use https://www.npmjs.com/package/secure-json-parse (as Fastify does under the hood)

axios equivalent of http requests?

I want to make the following http request
POST /v1/images HTTP/1.1
Host: api.medium.com
Authorization: Bearer 181d415f34379af07b2c11d144dfbe35d
Content-Type: multipart/form-data; boundary=FormBoundaryXYZ
Accept: application/json
Accept-Charset: utf-8
--FormBoundaryXYZ
Content-Disposition: form-data; name="image"; filename="filename.png"
Content-Type: image/png
IMAGE_DATA
--FormBoundaryXYZ--
It is Medium API
I have attempted following.
var axios = require("axios")
var data = (await axios("https://example.com/image.png")).data;
axios.post("https://api.medium.com/v1/images",{image: data},{
headers: {
"Content-Type" : "multipart/form-data",
"Authorization" : "Bearer " + process.env.key
}
}).then(x=>console.log(x.data))
And I get following error.
Error: Request failed with status code 400
It uses Medium API to upload image, I want to fetch a remote image, and convert it into multipart/form-data and upload it via API, the HTTP request seems confusing, I want the equivalent axios code, someone please help?
Try using FormData instead of plain object?
Example should be like this
const formData = new FormData();
formData.append('image', data);
axios.post('https://api.medium.com/v1/images', formData, {
headers: {
"Content-Type" : "multipart/form-data",
"Authorization" : "Bearer " + process.env.key
}
});
Reference: How to post a file from a form with Axios

Posted file has headers added to file content

I am trying to post files to server. It returns 200 OK response, but the content of files is not readable by programs. When I open those in text file, I see boundary and some headers are present in the content of file.
File starts with ----------------------------802523244934076832438189
Content-Disposition: form-data; name="file"; filename="Test1.png"
Content-Type: image/png
and Ends with ----------------------------802523244934076832438189--
My code is given below:
var formData = {
file:{
value: fs.createReadStream('./upload-folder/' + fileName),
options: {
filename: fileName,
contentType: req.body.attachment.mimeType //mimeType from JSON
}
}
};
var options = {
url: config.deployment.incidentUrl + '/attachment?filename=' + fileName,
method: "POST",
headers: { ContentType: "application/json"},
json: true,
formData: formData
};
request(options,
function (error, response, body) {
if (error) {
errorlog.error(`Error Message : PostAttachmentToCSMS : ${error}`);
}
else {
successlog.info(`Attachment posted for correlation Id: ${corIdFromJira}`);
}
});
I think it's because using formData is mutually exclusive with using json: true: the data is either multipart/form-data encoded or JSON-encoded, but it can't be both. The Content-Type header also isn't correct (although it's misspelled so technically not the problem).
Try this:
var options = {
url: config.deployment.incidentUrl + '/attachment?filename=' + fileName,
method: "POST",
formData: formData
};

How to POST with multipart/form-data header and FormData using fetch

This is a CURL example which works fine:
curl -X POST \
<url> \
-H 'authorization: Bearer <token>' \
-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
-F file=#algorithm.jpg \
-F userId=<userId>
I'm trying to reproduce this request using isomorphic-fetch.
I've tried the following code:
const formData = new FormData();
formData.append('file', file);
formData.append('userId', userId);
return fetch(`<url>`, {
method: 'POST',
headers: {
'Content-Length': file.length
'Authorization: Bearer <authorization token>',
'Content-Type': 'multipart/form-data'
},
body: formData
})`
I use fs.readFileSync in order to generate the file passed to FormData.
The previous example returns a 401 HTTP status code (unauthorized) with an error message saying that the userId embedded in the token (sent via header) does not match the userId passed from formData.
So my suspicion is that the FormData that arrives to the REST API is not adequately formed.
The problem may be related with the Content-Length header, but I didn't find a better way to calculate it (if I don't use the Content-Length header I get a 411 HTTP status code Content-Length header missing).
Could be the case that this is failing because of an incorrect value in the Content-Length header?
Any other suggestions on why this is failing or how to better debug it?
If further info is needed to clarify this problem, please just ask.
UPDATE
I've tried the form-data module in order to get the right Content-Length value using the method formData.getLengthSync()
However the problem remains the same (401 error HTTP status code response).
Just remove the Content-Length and Content-Type headers from your code as these headers will be set automatically by the browser.
If you open up your network inspector, run this code snippet, and submit the form you should see that the Content-Length is set correctly:
const foo = document.getElementById('foo')
foo.addEventListener('submit', (e) => {
e.preventDefault()
const formData = new FormData(foo)
formData.append('userId', 123)
fetch('//example.com', {
method: 'POST',
body: formData
})
})
<form id="foo">
<input id="file" type="file" name="file"/><br><br>
<button type="submit">Submit</button>
</form>
I hit my head against a similar wall, specifically using isomorphic-fetch on node to POST a multipart form. The key for me was finding .getHeaders(). Note that NPM description for form-data suggests that it'll "just work" without this, but it doesn't seem to, at least not in node (I think browsers inject header stuff?).
// image is a Buffer containing a PNG image
// auth is the authorization token
const form_data = new FormData();
form_data.append("image", png, {
filename: `image.png`,
contentType: 'application/octet-stream',
mimeType: 'application/octet-stream'
});
const headers = Object.assign({
'Accept': 'application/json',
'Authorization': auth,
}, form_data.getHeaders());
try {
const image_res = await fetch(url, {
method: 'POST',
headers: headers,
body: form_data
});
if (!image_res.ok) {
const out = await image_res.json();
console.dir(out);
return;
}
}
catch (e) {
console.error(`Chart image generation exception: ${e}`);
}

Resources