How do I send a filestream to localhost node-fetch? - javascript

On server side, I have this:
app.post('/testReadStream', function(req, res) {
var readStream = req.body;
readStream.pipe(process.stdout);
})
}).listen(443, () => console.log(`Listening on 443`));
I am making the following request from somewhere else:
let readStream = fs.createReadStream(path);
const fileSizeInBytes = fs.statSync(path).size;
fetch('https://localhost:443/testReadStream', {
method: 'POST',
headers: {
"Content-length": fileSizeInBytes
},
body: readStream
}).catch((err) => {
console.log(err);
})
I get the following error.
'request to https://localhost:443/testReadStream failed, reason: write EPROTO 4365467072:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../deps/openssl/openssl/ssl/record/ssl3_record.c:332:\n',
From this link https://github.com/openssl/openssl/issues/10938 it seems I did something wrong with filestreams. I copied it from here How to send a file in request node-fetch or Node?
What did i do wrong?

Looks like an SSL problem.
Can you try a simple string body
body: "text"

The SSL problem came from me putting "https" + localhost instead of "http://localhost". .-.
After that, all I had to change was req.body to req.

Related

JSON is becoming empty upon hitting server code

I am sending a fetch request with a JSON payload from my webpage like this:
let raw = JSON.stringify({"name":"James","favourite":"books"})
var requestOptions = {
method: 'POST',
body: raw
};
let send = () => {
fetch("http://mywebsite.herokuapp.com/send", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
On the server side I am getting an empty body {}. Here is the code I use to monitor that:
app.post('/send', (req, res) => {
console.log(req.body)
})
When I send the exact same code generated with Postman to server — somehow everything works fine, and I get the correct JSON. Please help me understand why that is.
On the server, req.body will be empty until you have middleware that matches the content type in the POST and can then read the body from the response stream and populate req.body with the results.
// middleware to read and parse JSON bodies
app.use(express.json());
app.post('/send', (req, res) => {
console.log(req.body);
res.send("ok");
});
And, then on the client side, you have to set the matching content-type, so the server-side middleware can match the content-type and read and parse it:
const requestOptions = {
method: 'POST',
body: raw,
headers: {
'Content-Type': 'application/json'
},
};

Sending an image with axios to Node.js and then using it further

I am trying to upload an image from the front-end, post it with axios to back-end (node.js) and then from there post it again to the GroupMe image service.
The main thing is to avoid using the API token in the front-end and so I was trying to first send a request to the back-end and then send the actual API request to the GroupMe image service which expects to get FormData of an image and sends back converted image URL.
I have tried to send FormData directly to the GroupMe image service from the front-end and everything works fine. However, in order to do so, I had to store the token in the front-end, which is not a good idea I believe.
The working code below:
let config = {
headers : {
'X-Access-Token': myToken,
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post'(https://image.groupme.com/pictures', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
What I need to happen instead is to send the request to the back-end like so:
axios.post(process.env.baseUrl+'/messengerRequests/upload-file/', fd, config)
.then((response)=>{
console.log(response)
})
.catch(err =>{
console.log(err.response)
})
And now in the back-end somehow be able to get that FormData and then create another post request to the GroupMe image service as I initially did in the front-end.
sendMessage: async(req, res) => {
axios.post('https://image.groupme.com/pictures', ???, config)
.then((response)=>{
res.send(response)
})
.catch(err =>{
console.log(err.response)
})
}
I do not know where it appears in the axios request. There is nothing in the req.body or req.params so I am not able to simply pass it further for the next post.
Is there a way somehow pass this FormData again?
Or maybe there is a way to safely use the token in the frond-end?
So, it should be relatively straightforward to post the image to GroupMe using Node.js and Express / Multer / Request. I've gone for Request rather than Axios on the backend since I'm more familiar with the API, but it's the same difference really.
Node.js Code (index.js)
const request = require("request");
const express = require("express");
const multer = require("multer");
const upload = multer();
const app = express();
const port = 3000;
const myToken = "" // Your API token goes here.
app.use(express.static("./"));
/* Here we take the image from the client and pass it on to GroupMe */
app.post("/uploadFile", upload.any(), (req, res) => {
sendImageToGroupMe(req, res);
});
function sendImageToGroupMe(req, res) {
const options = {
uri: "https://image.groupme.com/pictures",
body: req.files[0].buffer,
method: "POST",
headers: {
"X-Access-Token" : myToken
}
}
request(options, (err, response, body) => {
console.log("Request complete: Response: ", body);
if (err) {
console.error("Request err: ", err);
res.status(500).send("Upload failed: ", err.message);
} else {
res.status(201).send("Upload successful: GroupMe response: " + body);
}
});
}
app.listen(port);
Client side
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function uploadFile() {
var fileToUpload = document.querySelector('input[type=file]').files[0];
let config = {
headers : {
'Content-Type' : 'multipart/form-data'
}
}
let fd = new FormData()
fd.append('name', 'image')
fd.append('file', fileToUpload)
axios.post('http://localhost:3000/uploadFile', fd, config)
.then((response)=>{
console.log("Image posted successfully: ", response);
showOutput("Image posted successfully: " + response.data);
})
.catch(err =>{
console.error("Image post failed: ", err)
showOutput("Image post failed!");
})
}
function showOutput(html) {
document.getElementById("output").innerHTML = html;
}
</script>
</head>
<body style="margin:50px">
<input type="file" onchange="uploadFile()"><br>
<p id="output"></p>
</body>
</html>
All files go in the same directory. You can go to http://localhost:3000/ to test the index.html code, this will be served by the Node.js server as a static file.
I get a response like below from the GroupMe API:
{
"payload": {
"url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971",
"picture_url": "https://i.groupme.com/157x168.png.940f20356cd048c98478da2b181ee971"
}
}
We'll serve locally on port 3000, so to start the server:
node index.js
If you are using Express, you will need something to process the FormData. I have used multer for something similar before. I had to save the files into local storage, then resend the file with axios.

What is the cause of this 400 Error?

hey guys I am currently writing a small NodeJS script that takes an email address and attempts to figure out which social networks a particular email is associated with.
Here is my code:
const http = require('http');
const fetch = require('node-fetch');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});
fetch('https://api.fullcontact.com/v3/person.enrich',{
method: 'POST',
headers: {
"Authorization": "Bearer {iWotm_auth_token}"
},
body: JSON.stringify({
"email": "bart#fullcontact.com",
"webhookUrl": "http://www.fullcontact.com/hook"
})
}).then(res=>{
res.json().then(json=>{console.log(json);});
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
this is the error message I get Server running at http://127.0.0.1:3000/
{ status: 400, message: 'Access token supplied contains invalid characters' }
I am using the latest NodeJS version with Kali LInux, with the FullContact API. My question is, I did check if my api key contains invalid characters by running the dos2unix command and eliminating any possible white space so what is the cause of the error 400?
The steps I have taken so far:
Used bash to strip out all whitespace and the dos2unix command to eliminate any carriage returns. Then checked the api key under jwt.io to see if my API key is valid.
Your responses will be awesome.
The auth key you're providing to the API endpoint indeed contains invalid characters { and }.
Try again after removing them.
headers: {
"Authorization": "Bearer iWotm_auth_token"
}

Proxy request with Node.js: net::ERR_EMPTY_RESPONSE

Ultimately I am just trying to POST an image from the browser to a server. Unfortunately running into CORS issues, so for the moment, attempting to use our Node.js server as a proxy server.
I have this:
router.post('/image', function (req, res, next) {
const filename = uuid.v4();
const proxy = http.request({
method: 'PUT',
hostname: 'engci-maven.nabisco.com',
path: `/artifactory/cdt-repo/folder/${filename}`,
headers: {
'Authorization': 'Basic ' + Buffer.from('foo:bar').toString('base64'),
}
});
req.pipe(proxy).pipe(res).once('error', next);
});
the browser initiates the request, but I get an error in the browser saying I get an empty response, the error is:
Does anyone know why this error might occur? Is there something wrong with my proxy code in Node.js? Authorization should be fine, and the requeset url should be fine. Not sure what's going on.
Ok so this worked, but I am not really sure why:
router.post('/image', function (req, res, next) {
const filename = uuid.v4();
const proxy = http.request({
method: 'PUT',
hostname: 'engci-maven.nabisco.com',
path: `/artifactory/cdt-repo/folder/${filename}`,
headers: {
'Authorization': 'Basic ' + Buffer.from('foo:bar').toString('base64'),
}
}, function(resp){
resp.pipe(res).once('error', next);
});
req.pipe(proxy).once('error', next);
});
There is an explanation for why this works on this Node.js help thread:
https://github.com/nodejs/help/issues/760

Node.js equivalent of this curl request

I'm trying to use the HTML validator API. The curl examples work fine for me and I can run them find in Node as a child process. Here is the code for that:
var command = ('curl -H "Content-Type:text/html; charset=utf-8" --data-binary #' + file +
' https://validator.w3.org/nu/?out=json');
exec(command, function(err1, out, err2) {
console.log(out);
console.log('done');
});
However, when I tried to use a standard HTTP request, I couldn't get it working. I tried it with the Unirest library for Node. Here is the code I used:
var source = '<html><head><title>a</title></head><body>a</body></html>';
var url = 'http://validator.w3.org/nu/?out=json';
var Request = unirest.post(url);
Request.headers({'Content-Type': 'text/html', 'charset': 'utf-8'});
Request.send(source);
Request.end(res => console.log(res));
The response body is undefined and the response raw_body is empty. I don't know what I'm doing wrong and would appreciate any help.
Seems validator.w3.org won't respond to requests without a user-agent header. Add this header:
Request.headers({'Content-Type': 'text/html; charset=utf-8', 'user-agent': 'Node.js'});
Or use whatever useragent you want.
With super agent:
const request = require('superagent');
const body = '<html><head><title>a</title></head><body>a</body></html>';
request.post('https://validator.w3.org/nu/?out=json')
.set('Content-Type', 'text/html; charset=utf-8')
.send(body)
.end((err, res) => {
console.log('got body: ' + JSON.stringify(res.body));
});

Categories