I'm using Express 4, and I'm using a middleware http-proxy-middleware ( https://github.com/chimurai/http-proxy-middleware), and having the following issues
In normal way, I can do the following to manupulate the response before return to the client
app.get('/v1/users/:username', function(request, response, next) {
var username = request.params.username;
findUserByUsername(username, function(error, user) {
if (error) return next(error);
return response.render('user', user);
});
});
But how do I execute custom logic if I'm using proxy, let's say I want to manipulate some of the data before response to the client? Is there a good pattern to do that with this middleware ?
app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
Here is the backlink for the issue I put in github as well - https://github.com/chimurai/http-proxy-middleware/issues/97
Any help would be appreciated.
I think this is the correct way to do it according to the official documentation of http-proxy.
modify -response
app.use('/api', proxy({
target: 'http://www.example.org',
changeOrigin: true,
selfHandleResponse: true, // so that the onProxyRes takes care of sending the response
onProxyRes: function(proxyRes, req, res) {
var body = new Buffer('');
proxyRes.on('data', function(data) {
body = Buffer.concat([body, data]);
});
proxyRes.on('end', function() {
body = body.toString();
console.log("res from proxied server:", body);
res.end("my response to cli");
});
}
}));
here is my answer,
onProxyRes :function(proxyRes, req, res){
var _write = res.write;
var output;
var body = "";
proxyRes.on('data', function(data) {
data = data.toString('utf-8');
body += data;
});
res.write = function (data) {
try{
/*** something detect if data is all download.my data is json,so I can do by this***/
eval("output="+body)
output = mock.mock(output)
_write.call(res,JSON.stringify(output));
} catch (err) {}
}
}
add onProxyRes option on the http-proxy-middleware
use the data event on the proxyRes to get the output
then modify the output in res.write
Related
I have a api service in which there is a method to fetch data from the mongo db through node server. But i want to send the value of const userplant = localStorage.getItem("userplant"); along with the get request to the GET router in my node server so i can filter the data with a WHERE condition.
API.SERVICE.TS
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
return this.http.get(this.localURL + 'storagelocation/view', {headers:this.appHeader});
}
ROUTER.JS
router.get('/storagelocation/view', auth, function(req, res, next) {
StorageLocation.find({plantId : "5dd262a61120910d94326cc1"}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
I want the value of userplant next to the {plantId : "here"}
P.S.: My get request is perfectly working , i just want to send the const value along with it...
You can use queryParams to do that:
API.SERVICE.TS
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
return this.http.get(this.localURL + 'storagelocation/view' + '?userplant=' + userplant , {headers:this.appHeader});
}
ROUTER.JS
router.get('/storagelocation/view', auth, function(req, res, next) {
let userplant = req.query.userplant;
// you can use it now
StorageLocation.find({plantId : "5dd262a61120910d94326cc1"}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
Make the following changes in Angular, you can pass headers and parameters like this:
const httpOptions = {
headers: { 'Content-Type': 'application/json' },
params: {userplant:userplant}
};
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant");
return this.http.get(this.localURL + 'storagelocation/view',httpOptions);
}
Make the following change in Node Js,you have to use body parser.
router.get('/storagelocation/view', auth, function(req, res, next) {
var plantId=req.body.plantId;
StorageLocation.find({plantId : plantId}, function (err, events) {
if (err) return next(err);
res.json(events);
});
});
In query params you can send.
getStorageLocationsList(){
this.setHeader();
const userplant = localStorage.getItem("userplant"); //I Want to send this to the GET router
const params = new HttpParams().set('userplant ',userplant);
return this.http.get(this.localURL + 'storagelocation/view', {headers:this.appHeader, params: params});
}
I am trying to use node-http-proxy inside an AdonisJS controller, but I get the error
The "url" argument must be of type string. Received type function
The line causing the error is the proxy.web(request, response, { target: urlToProxy });
async proxy({ request, response }){
var resource = await Resource.query().where('uri', request.url()).with('service.user').with('userSecurity').first()
resource = resource.toJSON()
if(!resource.service.active){
return response.status(404).send(`Parent service '${resource.service.title}' is disabled`)
}
if(!resource.active){
return response.status(404).send(`Resource is disabled`)
}
if(resource.userSecurity.length <= 0) {
return response.status(403).send(`You are not permitted to access that resource. Contact ${resource.service.user.first_name} ${resource.service.user.last_name} (${resource.service.user.email})`)
}
var urlToProxy = url.resolve(resource.service.basepath, request.originalUrl())
var proxy = httpProxy.createProxyServer()
proxy.web(request, response, { target: urlToProxy });
}
In the end I got a bit closer but not a full fix. The getting close bit was to realise the http-proxy was delivering data via buffer so I had to do something like this
proxy.web(req, res, { target: data.service.basepath})
proxy.on('error', function(err, req, res){
console.log("There was an error", err)
})
proxy.on('proxyRes', async (proxyRes, request, response) =>{
var body = new Buffer('')
proxyRes.on('data', (data)=>{
body = Buffer.concat([body, data])
})
proxyRes.on('end', ()=>{
body = body.toString()
try{
res.send(body)
}catch(err){
}
})
});
However, I still could not get it to work as the controller was returning before http-proxy had completed the request.
In the end and probably for the best, I wrote a stand alone proxy app and used the main app just to validate JWT tokens before they go through the Proxy.
You were so close, I wanted to do something similar and wrapped the proxy in a promise so we can wait for the proxy to return before responding with our response:
const proxy = httpProxy.createProxyServer();
const prom = new Promise((resolve, reject) => {
proxy.web(request.request, response.response, {
target: urlToTarget
}, (e) => {
reject(e);
});
proxy.on('proxyRes', function (proxyRes, req, res) {
let body = [];
proxyRes.on('data', function (chunk) {
body.push(chunk);
});
proxyRes.on('end', function () {
body = Buffer.concat(body).toString();
resolve(body);
});
});
});
const result = await prom;
response.body(result);
return response;
I thought I'd give you a complete answer for anyone else that comes across this.
I want to make a request or api call on server 1, this server then automatically request to Server 2 and send the response back to server 1. I am using NodeJs and Express.
Example:
app.post('/api/Is', function(req, response, callback) {
})
I am calling that API in postmain as : http://localhost:3000//api/Is
So it should automatically go on http://localhost:5000//api/Is and send the response back to http://localhost:3000//api/Is call.
I should only call http://localhost:3000//api/Is and in backend code it will take request body and pass it to http://localhost:5000//api/Is and send the response back to http://localhost:3000//api/Is
I think you can consider use the the proxy lib like 'node-http-proxy', the most easy way.
otherwise, you must be transfer the request and response use 'http moudle', like this(no debug, not sure it will work perfectly~):
const http = require('http');
app.post('/api/Is', function(req, response, callback) {
const options = {
host:'localhost',
port:'5000',
path:'/api/Is',
method: 'POST'
// maybe need pass 'headers'?
};
let proxyBody = '';
const req2 = http.request(options, function(res2) {
res2.on('data',function(chunk){
proxyBody += chunk;
}).on('end', function(){
// here can feedback the result to client, like:
// const { headers } = res2;
// response.send(proxyBody)
});
});
// .on('error'){} here handle the error response
req2.end();
});
you need to use any library to make API call from server1 to server2. below code I am using fetch library.
To install the fetch library
npm install node-fetch --save
//SERVER1//
const fetch = require('node-fetch');
router.get("/api/Is", async (req, res) => {
try{
let {success, data} = await getDataFromServer2(req);
if(success) return res.send({success: true, data: data})
res.send({success: false})
}catch(e){
res.send({success: false})
}
});
function getDataFromServer2(req){
return fetch('http://localhost:5000//api/Is', {
method: 'post',
body: req,
headers: { 'Content-Type': 'application/json' },
}).then(res => res.json())
.then((response)=>{
return {
success: true,
data: response
}
}).catch((error) => {
throw new Error("unable to fetch the roles ")
})
}
I am creating a Rest API in Node.js and Express. It connects with remote HANA database & execute one query. Now I want to stream HTTP response so that I can send it to browser into chunks, rather than sending it completely since it's a very large data.
I tried something which is giving me no output. I don't know the reason. If I send the complete response to browser using response.send (data), it's working fine. But streaming is now working.
I have added code snippet below.
const express = require("express");
const APP = express();
const HANA_DB = require('hdb');
const BODY_PARSER = require("body-parser");
start();
function start() {
startServer();
initializeExpress();
APP.get("/data", function(request, response) {
var connection = HANA_DB.createClient({
host : "hostname",
port : "port",
user : "username",
password : "password"
});
connection.on('error', function (error) {
console.log("Error in database connection...");
});
connection.connect(function (error) {
if (error) {
console.log("Error in database connection...");
return;
}
var query = "SELECT * FROM TableName";
connection.exec(query, function(error, result) {
if(error) {
response.send("Getting error while fetching result...");
return;
}
//response.send(data);
var datalength = 0;
request.on('data', function(chunk) {
datalength += chunk.length;
console.log("DATA EVENT: " + datalength);
response.send(datalength);
})
.on('end', function() {
console.log("END EVENT: " + datalength);
response.send(datalength);
});
});
});
});
};
function initializeExpress() {
APP.all('/*', function(request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
response.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
response.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
APP.use(BODY_PARSER.json());
APP.use(BODY_PARSER.urlencoded({ extended: true }));
};
function startServer(config) {
var server = APP.listen("8081", function(error) {
if(error) {
console.log("Unable to connect to 127.0.0.1:8081");
return;
}
console.log("Server is listening at - 127.0.0.1:8081");
});
};
I think the logic you are using is wrong for the streaming the data.
Use res.write instead of res.send and you also have to read streaming data from your database instead of one time connection.exec
I am giving you an example code where you will get some idea about streaming data in Expressjs.
var http = require( 'http' );
http.createServer( function ( req, res ) {
res.writeHead( 200, {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked',
'X-Content-Type-Options': 'nosniff'
} );
res.write( 'Beginning\n' );
var count = 10;
var io = setInterval( function () {
res.write( 'Doing ' + count.toString() + '\n' );
count--;
if ( count === 0 ) {
res.end( 'Finished\n' );
clearInterval( io );
}
}, 1000 );
} ).listen( 8000 );
The problem is here request.on('data',. request refers to the browser request.
You cannot use streaming with .exec(), because the callback function of exec is called with the rows as parameters.
To use streaming, use the .execute() method, which passes a resultset to the callback function.
I never used hdb, so I cannot give the code to use.
I'm trying to create a proxy with node-http-proxy in Node.js that checks whether a request is authorized in a mongodb.
Basically, I created a middleware module for the node-http-proxy that I use like this:
httpProxy.createServer(
require('./example-middleware')(),
9005, 'localhost'
).listen(8005)
What the middleware module does is using mongojs to connect to mongodb and run a query to see if the user is authorized to access the resource:
module.exports = function(){
// Do something when first loaded!
console.log("Middleware loaded!");
return function (req, res, next) {
var record = extractCredentials(req);
var query = -- Db query --
//Debug:
log("Query result", query);
db.authList.find(query).sort({
"url.len": -1
}, function(err, docs){
console.log(docs);
// Return the creator for the longest matching path:
if(docs.length > 0) {
console.log("User confirmed!");
next();
} else {
console.log("User not confirmed!");
res.writeHead(403, {
'Content-Type': 'text/plain'
});
res.write('You are not allowed to access this resource...');
res.end();
}
});
}
}
Now the problem is that as soon as I add the asynchronous call to mongodb using mongojs the proxy hangs and never send the response back.
To clarify: on a "User not confirmed" everything works fine and the 403 is returned. On a "user confirmed" however I see the log but the browser then hangs forever and the request isn't proxied.
Now, if I remove the "user confirmed" and next() part outside of a callback it does work:
module.exports = function(){
// Do something when first loaded!
console.log("Middleware loaded!");
return function (req, res, next) {
var record = extractCredentials(req);
var query = --- query ---
console.log("User confirmed!");
next();
}
but I can't do that since the mongojs query is meant (rightfully I guess) to be executed asynchronously, the callback being triggered only when the db replied...
I also tried the version without using a middleware:
http.createServer(function (req, res) {
// run the async query here!
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9000
});
}).listen(8001);
But that did not help either...
Any clue? Note that I'm new to node.js so I suspect a misunderstanding on my side...
Found the answer, actually the catch is that the request needs to be buffered:
httpProxy.createServer(function (req, res, proxy) {
// ignore favicon
if (req.url === '/favicon.ico') {
res.writeHead(200, {
'Content-Type': 'image/x-icon'
} );
res.end();
console.log('favicon requested');
return;
}
var credentials = extractCredentials(req);
console.log(credentials);
var buffer = httpProxy.buffer(req);
checkRequest(credentials, function(user){
if(user == ...) {
console.log("Access granted!");
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 9005,
buffer: buffer
});
} else {
console.log("Access denied!");
res.writeHead(403, {
"Content-Type": "text/plain"
});
res.write("You are not allowed to access this resource...");
res.end();
}
});
}).listen(8005);
Two problems:
You're not calling next(); in the else case of your sort callback.
The second parameter to your sort callback is a Cursor, not an array of documents. As such, docs.length > 0 is never true and the code always follows the else path.