I am trying to forward my request from my NodeJS Proxy server to another server. The request I am trying to forward contains FormData()
I created FormData as per MDN docs
const payload = new FormData();
payload.append('addresses', file); // <---- UPLOADED FILE
payload.append('reason', 'reason');
payload.append('type', 'type');
This is how I am essentially sending the request to my NodeJS server
fetch("localhost:3000/v1/addresses", {
method: 'PUT',
body: payload
});
NodeJS Server at localhost:3000
const multer = require('multer');
const upload = multer();
app.put('/v1/addresses', upload.single('addresses'), (req, res) => {
let options = {
host: 'localhost',
method: 'PUT',
port: 8000,
path: req.originalUrl,
headers: req.headers,
formData: {
reason: req.body.reason,
type: req.body.type,
}
};
console.log("reason", req.body.reason) // "reason"
console.log("type", req.body.type) // "type"
console.log("addresses", req.file) // FILE OBJECT
const request = http.request(options, response => {
res.writeHead(response.statusCode, response.headers);
response.pipe(res);
});
request.end();
})
The code above, I'm not sure how to send over the actual file to the other service. Also, I am NOT seeing the reason and and type that I've passed over to the service.
What's also strange is that I see this in the incoming request in my NON- PROXY server
PUT /v1/addresses HTTP/1.1
Host: localhost:3000
Connection: keep-alive
Content-Length: 932
Sec-Ch-Ua: "Google Chrome";v="89", "Chromium";v="89", ";Not A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryt2p0AWOqJCnz95hg
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/blocklist
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
[object Object]
So after lots of searching and experimenting, this post actually provided me with the answer
Here is the code from the post.
const express = require("express");
const app = express();
const bodyParser = require('body-parser');
var multer = require('multer')();
const FormData = require('form-data');
const axios = require('axios');
const fs = require('fs');
app.use(bodyParser.json());
app.post('/fileUpload' , multer.single('fileFieldName'), (req , res) => {
const fileRecievedFromClient = req.file; //File Object sent in 'fileFieldName' field in multipart/form-data
console.log(req.file)
let form = new FormData();
form.append('fileFieldName', fileRecievedFromClient.buffer, fileRecievedFromClient.originalname);
axios.post('http://server2url/fileUploadToServer2', form, {
headers: {
'Content-Type': `multipart/form-data; boundary=${form._boundary}`
}
}).then((responseFromServer2) => {
res.send("SUCCESS")
}).catch((err) => {
res.send("ERROR")
})
})
const server = app.listen(3000, function () {
console.log('Server listening on port 3000');
});
Related
What I've done: I tried to post json from angular to nodejs but it failed. I tried to search for solution of "undefined post nodejs json" and "options instead of post", and changed my header to exclude the [option], also tried to parse the json data I tried to set, but nothing is useful.
The problem: In the angular code below, I did not get response, there was no output of the "console.log()", I wonder what is wrong. And the json.body I reuqired in app.js returns "undefined"
I checked the browser, first it sends [options] request and gets 200OK, but next when it sends [post], but no status is returned :
OPTIONS /api/postData HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Referer: http://127.0.0.1:4200/
Origin: http://127.0.0.1:4200
Connection: keep-alive
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
POST http://127.0.0.1:3000/api/postData
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 27
Origin: http://127.0.0.1:4200
Connection: keep-alive
Referer: http://127.0.0.1:4200/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
Is there any mistake in the code?
angular code: component.ts
this.http.post<any>("http://127.0.0.1:3000/api/postData", loc_json).subscribe(response =>{
console.log("Post to nodejs from angular")
console.log(response);
});
nodejs using express: app.js
const express = require('express')
const api_helper = require('./API_helper')
const port = 3000
const cors = require('cors')
const app = express()
app.use(cors())
// allowCrossDomain = function(req, res, next) {
// res.header('Access-Control-Allow-Origin', '*');
// res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
// res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
// if ('OPTIONS' === req.method) {
// res.send(200);
// } else {
// next();
// }
// };
// app.use(allowCrossDomain);
app.get('/', (req, res, next) => {
res.send("wtffffffffffffffffff");//send to the page
})
app.post('/getAPIResponse', (req, res, next) => {
api_helper.make_API_call('https://jsonplaceholder.typicode.com/posts')
.then(response => {
res.json(response)
})
.catch(error => {
res.send(error)
})
})
//angular --> nodejs
app.post('/api/postData', function(request, response){
//console.log("postData on nodejs 3000");
// const loc_nodejs = req.body;
// console.log("loc_nodejs.loc ", loc_nodejs);
console.log(request.body);
})
app.listen(port, () => console.log(`NodeJS App listening on port ${port}!`))
Because you are using Sec-Fetch-Mode: cors in your request, then your API in Express needs to have CORS enabled and configured correctly. If you are doing local development, I would turn CORS off and enable it in production, but you also may be running two processes for the Angular app and the Express app. In that case, follow the documentation for Express to enable CORS on the API via the documentation below.
https://expressjs.com/en/resources/middleware/cors.html
I added these in my code and they appeared deprecated in the pic, but it does work
const bp = require('body-parser');
app.use(bp.json())
app.use(bp.urlencoded({ extended: true }))\
I have this react-app that is connected to a node/express server. The node/express server all it has is app.get(api)
I connected it to 4 different apis, it gets the data and sends simply returns it to the front-end/client.
When I was building it and running it locally, it worked no problem. I would get the json objects and my front-end cleans the data and displays it. As soon as I deployed my server to heroku, it stopped working. I'm using axios to make the requests and I changed it from http://localhost/3000 to the url that heroku gave me. Now instead of getting my json data I get this error:
If it helps, when I run the url directly in my browser it works, I do see the data like I'm supposed to. Why is that? Thank you so much to whoever helps me!
As requested, this is a part of my express server, the other two routes are basically the same thing but of course to different APIs.
const express = require("express");
const axios = require("axios");
const app = express();
const cors = require("cors");
require("dotenv").config();
// This enables our app (server 3000) to make requests
// can also do "*" to allow anyone to make them but not recommended
// for security reasons
app.use(
cors({
origin: "*",
}),
);
app.get("/walmart/:item", (req, res) => {
let url = `https://www.walmart.com/search/api/preso?prg=desktop&page=1&query=${req.params.item}`;
var config = {
method: "get",
url: url,
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0",
Accept: "application/json",
"Accept-Language": "en-US,en;q=0.5",
"content-type": "application/json",
wm_client_ip: "",
Connection: "keep-alive",
Cookie: process.env.WALMART_COOKIE,
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
},
};
axios(config)
.then((response) => {
res.send(response.data.items);
})
.catch((err) => {
res.send(err);
});
});
app.get("/wholefoods/:item", (req, res) => {
let url = `https://www.wholefoodsmarket.com/api/search?text=${req.params.item}&store=10152&limit=60&offset=0`;
axios
.get(url)
.then((response) => {
res.send(response.data.results);
})
.catch((err) => {
res.send(err);
});
});
and this is the data I should be receiving when I call those routes from my react app (and the one I get when I use my browser at the moment)
this is the request i want to perform:
POST /v1/oauth2/token HTTP/1.1
Host: api.sandbox.paypal.com
Accept: application/json
Accept-Language: en_US
Authorization: Basic cGF5cGFsaWQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
I tried it in nodejs using this code:
paypalSignIn = function(){
var username = process.env.PAYPALID;
var password = process.env.PAYPALSECRET;
var auth = 'Basic ' + Buffer.from(username + ':' + password).toString('base64');
// new Buffer() is deprecated from v6
// auth is: 'Basic VGVzdDoxMjM='
var post_data = querystring.stringify({
'grant_type' : 'client_credentials',
});
var header = {'Accept': 'application/json', 'Authorization': auth, 'Accept-Language': 'en_US'};
const options = {
hostname: 'api.sandbox.paypal.com',
port: 443,
path: '/v1/oauth2/token',
method: 'POST',
headers: header,
}
var post_req = http.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('Response: ' + chunk);
});
});
post_req.write(post_data);
post_req.end();
}
Unfortunately i'm getting the following error:
Error: socket hang up
Try using the https module (it's not enough to set port 443, you have to use the HTTPS protocol to connect to an HTTPS endpoint).
I also noticed you didn't set the Content-Type header. It depends on the API, but that may cause problems for you too.
Finally, I'd consider using a library that wraps http/https like node-fetch, bent, or axios for this rather than the standard library directly. It can handle things like writing to the socket, setting the Content-Length header, etc.
I can not access the "Authorization" header in each HTTP request on my Apollo-Server, implemented with express.
Here is my setup of express, Apollo-Server, CORS, etc.
const corsConfig = {
credentials: true,
allowedHeaders: ['Authorization'],
exposedHeaders: ['Authorization']
};
const app = express()
const server = new ApolloServer({
schema,
context: ({ req }) => {
return {
req
};
}
});
server.applyMiddleware({
app,
path,
cors: corsConfig
});
http.createServer(app).listen(port, () => logger.info(`Service started on port ${port}`));
And inside my resolvers, I bring in the context, particularly the req object (this is an example graphQL endpoint resolver):
const exampleQuery = async (parent, input , { req }) => {
console.log(req.headers);
/*
The output of this log:
{
'content-type': 'application/json',
accept: '*/*',
'content-length': '59',
'user-agent': 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)',
'accept-encoding': 'gzip,deflate',
connection: 'close',
host: 'localhost:3301',
'Access-Control-Allow-Headers': 'Authorization',
'Access-Control-Expose-Headers': 'Authorization'
}
*/
}
I have sent requests to this endpoint, with an "Authorization" header, containing a token as the value. However, the Authorization header is not in the req.headers object (in fact, it's not in the entire req object either). I am certain that my Postman/Insomnia HTTP requests to this endpoint are sending out the Authorization header, however it seems to be not getting through my Apollo-Server.
Anyone have any insight as to why the Authorization header is not going through?
SOLUTION:
The problem was actually that I am using an Apollo federated microservices architecture, which requires additional configuration on the gateway to pass the Authorization header onto the individual microservices, where the resolvers are. You have to add the buildService function inside the ApolloGateway constructor, where you specify that a RemoteGraphQLDataSource willSendRequest of context.req.headers.authentication to the underlying microservices
It works as expected, E.g.
server.ts:
import { ApolloServer, gql, makeExecutableSchema } from 'apollo-server-express';
import express from 'express';
import http from 'http';
const corsConfig = {
credentials: true,
allowedHeaders: ['Authorization'],
exposedHeaders: ['Authorization'],
};
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: (_, __, { req }) => {
console.log(req.headers);
return 'world';
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();
const path = '/graphql';
const port = 3000;
const server = new ApolloServer({
schema,
context: ({ req }) => {
return {
req,
};
},
});
server.applyMiddleware({ app, path, cors: corsConfig });
http.createServer(app).listen(port, () => console.info(`Service started on port ${port}`));
Send a GraphQL query HTTP request via curl:
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer abc123" --data '{ "query": "{ hello }" }' http://localhost:3000/graphql
{"data":{"hello":"world"}}
Server-side logs:
Service started on port 3000
{ host: 'localhost:3000',
'user-agent': 'curl/7.54.0',
accept: '*/*',
'content-type': 'application/json',
authorization: 'Bearer abc123',
'content-length': '24' }
So basically I have created my own "Imgur" where I enter a image URL etc.
https://images.hdsydsvenskan.se/980x588/Q2GO5t2lmKtW6WwGuEVQNTPmt4o.jpg
The issue is that it worked before but now I am getting errors (And I haven't change the code at all - My guess that it has to do with some URLS?) that are saying:
{ Error: read ECONNRESET
at TLSWrap.onread (net.js:622:25) errno: 'ECONNRESET', code: 'ECONNRESET', syscall: 'read'}
and honestly I don't see issue because it does work on other pictures but if I choose etc the one below. It keeps giving me that issue.
const fs = require('fs')
const path = require('path')
const request = require('request')
const express = require('express')
const app = express()
const PORT = process.env.PORT || 8888
// Path to images directory
const images = path.join(__dirname, 'images')
// JSON parsing middleware
app.use(express.json())
app.post('/newimage', (req, res) => {
const imageUrl = req.body.image_url
const imageName = req.body.image_name
request({
url: imageUrl,
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
})
.on('error', (err) => {
console.log(err)
console.log(res.status())
res.status(500)
res.send('not ok')
})
.on('response', (response) => {
console.log(`Response for ${imageUrl}`)
console.log(response.statusCode + '\n')
res.send('ok')
})
.pipe(fs.createWriteStream(path.join(images, imageName + '.png')))
})
app.get('/images/:imagename', (req, res) => {
const imageName = req.params.imagename
const filePath = path.join(images, imageName + '.png')
console.log(imageName)
console.log(filePath)
if (fs.existsSync(filePath)) {
fs.createReadStream(filePath)
.pipe(res)
} else {
res.send('No image found')
}
})
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`)
console.log(`http://xx.xxx.xx.xx:${PORT}`)
})
If anyone has any clue how I can solve it! I would appreciate it!
There's something funky going on with the Adidas-server that you mention in your comment, it requires a particular list of headers to be set before it works (requests time out for me otherwise):
request({
url: imageUrl,
headers: {
Accept: '*/*',
'Accept-Encoding': 'gzip, deflate',
Connection: 'keep-alive',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'
}
})
It looks like the Varnish cache that they're using might be misconfigured (or it's some strange attempt to try and block scrapers).