I am writing a custom middleware that generates a cryptographic signature of every request (it is very similiar to the authentication mechanism used by AWS API v4). In order for this signature to be correctly generated, I must fetch the entire raw body of the HTTP request.
I am also using BodyParser, which is registered after my custom middleware.
My custom middleware can be represented like this:
// libs/simplifiedSignatureCheckerMiddleware.js
module.exports = function (req, res, next){
// simple and fast hashing stuff
var payload = '';
req.on('data', function(chunk) { payload += chunk }, null);
req.on('end', function(){
// hmac stuff
console.log(payload);
var ok = true; // ...
if(ok)
next();
else
next("Bad")
});
}
This is how I use it on the server.
// simpleServer.js
// BASE SETUP
// =============================================================================
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var jsonStream = require('express-jsonstream');
var nconf = require('nconf');
var https = require('https');
var fs = require('fs');
// load configurations
nconf.argv().env();
nconf.file({file: 'config.json'});
app.use(require('./libs/simplifiedSignatureCheckerMiddleware'));
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(jsonStream());
// ROUTES FOR API
// =============================================================================
var router = express.Router();
router.post('/api/', function (req, res) {
var param1 = req.body.param1 || "";
var param2 = req.body.param2 || "";
res.json({message: 'welcome', one: param1, two: param2 });
});
// REGISTER ROUTES
app.use(router);
// START THE SERVER
// =============================================================================
https.createServer({
key: fs.readFileSync('./key.pem'),
cert: fs.readFileSync('./cert.pem')
}, app).listen(nconf.get('http:port'));
console.log("APIs listening on port " + nconf.get('http:port'));
As you can verify, the raw body is written successfully to the console by the middleware, BUT the request will never be processed by the registered route and the connection hangs forever.
Do you have any clue on how to solve this problem?
Thanks in advance.
Ok, since the only feasible way to solve this problem seems to be by modifying the original source code of bodyParser, I have forked it.
https://github.com/emanuelecasadio/body-parser-rawbody
This fork exposes the raw body of the request as a field named rawBody. As you can see, there is only ONE extra line of code.
You can install it by using npm install body-parser-rawbody.
EDIT
Another option is to use the bodyParser like this, as noted by dougwilson here: https://github.com/expressjs/body-parser/issues/83#issuecomment-80784100
app.use(bodyParser.json({verify:function(req,res,buf){req.rawBody=buf}}))
I haven't personally tried this option and I do not know if it works.
Related
I am trying to start my project via launching server.js but I am getting error:"cannot GET /"
even after I made an app.get() route in my server.js
I am using also "body-parser" as a middleware, and "cors"
server.js:
// Setup empty JS object to act as endpoint for all routes
const projectData = {};
// Require Express to run server and routes
const express = require('express');
// Start up an instance of app
const app = express();
/* Middleware*/
//Here we are configuring express to use body-parser as middle-ware.
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Cors for cross origin allowance
const cors = require('cors');
app.use(cors());
// Initialize the main project folder
app.use(express.static('views'));
const port = 8080;
app.use(express.static('dist'));
// Setup Server
const server=app.listen(port, ()=>{console.log(`running on localhost: ${port}`)});
app.get('/all', sendData);
function sendData (request, response) {
response.send(projectData);
};
// TODO-ROUTES!
app.post('/add', Info);
function Info(req, res) {
projectData['date'] = req.body.date;
projectData['temp'] = req.body.temp;
projectData['content'] = req.body.content;
res.send(projectData);
}
module.exports = server;
I made a POST request to /add and it works. Then I call GET /all and also work. The error cannot GET / is because you are requesting an endpoint that not exists.
Just add a dummy data to your Fn() if you want to see some output on GET request wihtout making any post request
Here is my solution
app.get('/', (req, res) => {
res.redirect('/all');
})
we need this because you do not have any root URL set, so we set the root URL to redirect to /all
2nd step then we add a dummy data in the projectData
var projectData = {
date = new Date().getDate();,
temp = 10,
content="This is Test Content"
}
This way when you call 'http://localhost:8080/' you shall get redirected to http://localhost:8080/all and instead of empty {} you shall see the dummy data.
I am working on Salesforce and Slack integration. And I don't know much about javascript and its related technologies. Could you please look into the code and let me know whats missing?
// Import express and request moduless
var express = require('express');
var request = require('request');
var url = require('url');
var clientId = '****';
var clientSecret = '****';
var SF_LOGIN_URL = "http://login.salesforce.com";
var SF_CLIENT_ID = "****";
// We define the port we want to listen to. Logically this has to be the same port than we specified on ngrok.
const PORT=4390;
// Instantiates Express and assigns our app variable to it
var app = express();
app.enable('trust proxy');
//var server = http.createServer(app);
//Lets start our server
app.listen(PORT, function () {
//Callback triggered when server is successfully listening.
console.log("Example app listening on port " + PORT);
});
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// Route the endpoint that our slash command will point to and send back a simple response to indicate that ngrok is working
app.post('/oauth', function(req, res) {
oauth(req, res);
});
function oauth(req, res){
res.redirect(200, `${SF_LOGIN_URL}/services/oauth2/authorize?response_type=code&client_id=${SF_CLIENT_ID}&redirect_uri=****/oauth&display=touch}`);
console.log(url.location.href);
}
It looks to me like you're redirecting an authorization request to Salesforce, and asking Salesforce.com (SFDC) to redirect it back to ****/oauth (from the redirect_uri= query parameter to the SFDC URL.
Are you hoping it will get redirected back to your own /oauth endpoint?
If so, it's possible SFDC is redirecting it with a GET operation rather than a POST operation. Be aware that the parameters to a GET show up in req.params rather than req.body.
Try implementing a get() handler to see if you get something workable.
app.get('/oauth', function(req, res) {
console.log ('GET /oauth', req.params)
/* do something here */
});
I am trying to change the below code using connect module and connect-route. Currently it is written in express.
//app.js
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use("/shop/rackOne", rackOne);
app.listen(1000);
//rackOne.js
var express = require('express');
var shoes = require('./shoes.js');
var router = express.Router();
router.all('/stock', shoes);
//shoes.js
function shoes(req, res, next) {
var body = req.body;
}
module.exports = shoes;
Issues which I am facing
Not able to read body data. In connect, no req.body is available.
No .all router is available.
Code change using connect
var connectRoute = require('connect-route'),
connect = require('connect'),
app = connect();
app.use(connectRoute(function (router) {
router.get('/shop/rackOne', rackOne);
});
I am not sure. Is this the correct way to do it. Any help on this will be really helpful.
Well, I'm not sure to understand what you want here, but I'll try to answer.
From the npm page of connect, all I can offer you to try is this :
var connectRoute = require('connect-route');
var connect = require('connect');
var app = connect();
app.use(function yourFunctionFromMiddleware(req, res, next) {
// use req, res as you wish
next();
});
I used connect once, and I used this code, which worked properly for what I needed back there, not sure what you are trying to do here though.
here is my code
var express=require("express");
var app=express();
var port=8181;
app.use(express.static(__dirname));
app.listen(port);
it is serving static file properly
I want to log when a file with an extension .xls is being requested
how can i achieve it ?
The path core module gives you the tools to deal with this. So, just put this logic in a middleware before your static middleware, like:
var express = require("express");
var path = require("path");
var app = express();
var port = 8181;
app.use(function (req, res, next) {
var filename = path.basename(req.url);
var extension = path.extname(filename);
if (extension === '.css')
console.log("The file " + filename + " was requested.");
next();
});
app.use(express.static(__dirname));
app.listen(port);
You want to log what serve-static (express.static) gives in response. There're several ways to do this.
Method Ⅰ A middleware for manual checks.
You may put (app.use) a middleware that logs a request if it's for express.static, before express.static. Rodrigo Medeiros' answer does this. But this way, you have to rewrite the code for checks when the options for the serve-static middleware changes, which might be a maintaining issue.
Method Ⅱ Hooking into express.static; leaking info out of it.
Well, express.static knows what files it gives best. It just, unfortunately, does not let us know and log it. But there's a hack for this: the setHeaders option, which is a callback function supposedly used to set custom response headers. It's called when express.static makes a response and gets enough information to log what you want to.
const express = require("express");
const path = require("path");
const app = express();
const asset_dir_path = "assets/";
app.use(express.static(asset_dir_path, {
index: false,
setHeaders: (response, file_path, file_stats) => {
// This function is called when “serve-static” makes a response.
// Note that `file_path` is an absolute path.
// Logging work
const relative_path = path.join(asset_dir_path, path.relative(asset_dir_path, file_path));
console.info(`#${Date.now()}`, "GAVE\t\t", relative_path);
}
}));
Just do
var express=require("express");
var app=express();
var port=8181;
app.use(function(req, res, next) {
// check for .xls extension
console.log(req.originalUrl);
next();
}, express.static(__dirname));
app.listen(port);
I am building a cross system admin app, which will be used as an admin tool for multiple backend systems. The app is built on top of Mean.js.
I have setup a /proxy route using "express-http-proxy" to send all sub-routes to their respective backend system endpoints. However, I need to have each request authenticated within my admin app and then decorated with the targeted backendSystem credentials before the "express-http-proxy" can continue. Here's an example of my /proxy route...
app.use('/proxy', users.requiresLogin, expressHttpProxy(config.backendSystem.host, {
forwardPath: function (req) {
return '/1.0' + require('url').parse(req.url).path;
},
decorateRequest: function (req) {
req.headers['content-type'] = 'application/json';
req.headers['backend-system-id'] = config.backendSystem.id;
req.headers['backend-system-key'] = config.backendSystem.key;
return req;
}
}));
NOTE:
Currently the backendSystem credentials are stored based on the environment my admin app is ran in. However, in the future the backendSystem credentials will be specified by the user, and this /proxy route will differently than what is currently shown.
THE ISSUE:
Proxy routes that require data within the request body don't work.
e.g. POST /comments {"user": user_id, "text": "rabble rabble rabble"}
WHAT I'VE FOUND:
bodyParser.json() and "express-https-proxy" don't play nice. I've confirmed this by removing bodyParser.json() from express.js.
However, this isn't a full solution since almost all of my other routes need bodyParser.json, e.g. /auth/signin.
Does anyone have a clean way that I can make a route exception for my /proxy route so that bodyParser.json won't be called for it?
As far as I understand, the root of problem is so:
if you were reading a POST request by pure node, you should be using a code like this
if (req.method == 'POST') {
console.log("POST");
var body = '';
req.on('data', function (data) {
body += data;
console.log("Partial body: " + body);
});
req.on('end', function () {
console.log("Body: " + body);
});
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('post received');
}
in other words, you need to use the req.on('data') & req.on('end') events.
but the problem is,that you can use this code only once. after the 'end' is called, the request is consumed.
so then you use bodyParser , it consumes the request, and the proxy have nothing to do with it.
actually, in my opinion, the proxy wait for the 'data' event to appear , but it will newer happen, so the code halts.
The solution:
you need to 're-enable' the events. I used this code and it works for me.
var express = require('express');
var bodyParser = require('body-parser');
var http = require('http');
//call for proxy package
var devRest = require('dev-rest-proxy');
//init express (as default)
var users = require('./routes/users');
var app = express();
app.use(bodyParser.json());
//set the proxy listening port
app.set('port', 8080);
//process the POST request
app.post('/users/*', function(req, res) {
//just print the body. do some logic with it
console.log("req.body: ",req.body);
//remove listeners set by bodyParser
req.removeAllListeners('data');
req.removeAllListeners('end');
//add new listeners for the proxy to use
process.nextTick(function () {
if(req.body) {
req.emit('data', JSON.stringify(req.body));
}
req.emit('end');
});
//forward the request to another server
devRest.proxy(req,res, 'localhost', 3000);
});
//start the proxy server
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
module.exports = app;
the solution found on schumacher-m post (github of nodejitsu)
I was able to resolve my issue by adding a regex that excluded my /proxy route to where bodyParser.json was being added within express.js. I found that from this answer
While this approach doesn't scale well, it solved my immediate issue.
I get it works by converting the data into query string using 3rd party query-string as follows:
proxyReqBodyDecorator: function(bodyContent, srcReq) {
return (queryString.stringify(bodyContent));
}
Have tried JSON.stringify but not working, need the data in the following format
array_field=val1&array_field=val2&array_field=val3......
To modify the request body, do this with the latest express-http-proxy v1.6.2:
const express = require('express');
const proxy = require('express-http-proxy');
const bodyParser = require('body-parser');
const conf = {
proxyHost: 'some.example.net:9200',
proxyOptions: {
proxyReqBodyDecorator: modifyRequestBody,
preserveHostHdr: true,
parseReqBody: true
},
port: 8073
};
var app = express();
app.use('/proxy', proxy(conf.proxyHost, conf.proxyOptions));
function modifyRequestBody(body, srcReq) {
if(srcReq.method.match(/^(GET|POST)$/i)) {
try {
// convert buffer to string, then to object
var str = Buffer.from(body).toString('utf-8');
var reqBody = JSON.parse(str);
if(someCondition)) {
reqBody.addStuff = 'whatever';
body = reqBody; // return modified body as object
}
} catch(error) {
console.log('- error: ' + JSON.stringify(error));
}
}
return body; // return original buffer, or modified object
}
app.listen(conf.port, function () {
log('app listening on port ' + conf.port);
});
You can fill the proxyReq.bodyContent inside the decorateRequest method with the JSON-ed data from originalReq.body to be correctly POST'ed:
app.use('/proxy', users.requiresLogin, expressHttpProxy(config.backendSystem.host, {
...
...
decorateRequest: function (proxyReq, originalReq) {
...
...
if (originalReq.body) {
proxyReq.bodyContent = JSON.stringify(originalReq.body);
}
return proxyReq;
}
...
...
}));