How to handle routes that have # in nodejs with expressjs? - javascript

The question is related to: when I refresh the page, not the page loads. Angularjs + Nodejs
But in this case I disable html5mode and , therefore the url the symbol appears #. I want to have control access to certain sections of the code that I show below, but do not know how to handle the #
app.get(['/homepage', '/recoveryPassword', '/manageAccount', '/shop/:itemId/cat/:categoryId', '/shop/:shopName', '/manageMyShops', '/manageMyShops/id/:shopId', '/manageWeb'], isLoggedIn, function (req, res) {
res.end();
});

I am reluctant to post this because I think this is NOT a real solution to OP's problem. To me this is a hack, so use it at your own risk.
To start with - it is unconventional for REST URL to have special characters like '#' in them. While I believe there is no standard way of naming the endpoints but it is recommended to make sure the URLs follow these rules;
all lower-case (because url are not case-sensitive)
no special characters
no camel case
Anyway, my approach is.
On the server code;
Assuming you are defining a path /config# then replace the '#' char with something unique e.g. _bbb Unique enough? you judge, just make sure it doesn't clash with other endpoints.
Add an express layer to decode the URL's special character and replace the special character with the unique word you have given in point 1. (This will be more clear when you see the code).
Client code:
Encode the URL. Why? Because URL can't have '#', because it is a reserved character. So it has to be encoded as '%23' instead.
Example:
Client:
"use strict";
let request = require("request");
let req = {
url: `localhost:4444/${encodeURIComponent('config#')}`,
proxy: 'http://localhost:4444',
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
};
request(req, function (err, res, body) {
this.config = JSON.parse(body);
console.log("response => " + this.config);
});
Server:
"use strict";
var express = require("express");
var bodyParser = require("body-parser");
var app = express();
var config = require('config');
app.use(bodyParser.json());
app.use((req, res, next) => {
let decoded_url = decodeURIComponent(req.url);
req.url = decoded_url.replace('#', '_bbb');
next();
});
app.get('/config_bbb', function(req, res){
res.json('{name: test}');
});
// Start the server
app.set('port', 4444);
app.listen(app.get('port'), "0.0.0.0", function() {
console.log('started');
});
Output:
response => {name: test}
The idea here is. When the client calls the endpoint, yes the URL is still with /config# but you encode it so that it looks like /config%23.
See:
http://www.w3schools.com/tags/ref_urlencode.asp
Then when the request comes through into the server, the layer you have added in expressJS would decode the URL from /config%23 to /config# and you replace the # char with something unique. Let say _bbb, so that the final URL is /config_bbb which gets routed to the actual endpoint.
Unfortunately express doesn't like endpoints to have special characters in them, if you don't replace the # to be something recognizable then you will find that it doesn't get routed it properly even thou the URL matches.

Related

Node.js unable to read POST JSON data in my webhook

I have a node.js + Express application. It has a webhook that I have provided to a third party service. The service sends a POST request to my webhook with JSON body which looks something like this:
{"split_info" : "null", "customerName" : "Merchant Name",
"additionalCharges" : "null", "paymentMode":"CC",
"hash":"a31ff1b91fd9b8ae9c82f38b02348d21fsdfd86cc828ac9a0acf82050996372cc656de3db0fe3bf9af52b73a182a77787241f3e19ec893391607301b03e70db8",
"status" : "Release Payment", "paymentId" : "551731" ,
"productInfo":"productInfo", "customerEmail":"test#gmail.com",
"customerPhone":"9876543212", "merchantTransactionId":"jnn",
"amount":"100.0", "udf2":"null", "notificationId" :"4", "udf1":"null",
"udf5":"null", "udf4":"null", "udf3":"null","error_Message":"No
Error"}
I am using body-parser module to read POST data. However when I do req.body it gives [object Object], if I do JSON.stringify(req.body), it gives {} i.e. empty. If I try to access the keys in the response like req.body.paymentMode then it gives undefined.
Here is my router code for the webhook: mywebhook.js
var express = require('express');
var router = express.Router();
router.post('/success', function(req, res){
//this is where I need to strip the JSON request
//req.body or JSON.stringify(req.body) or anything that works
//if everything is okay then I send
res.sendStatus(200);
});
module.exports = router;
My app.js looks like this:
var express = require('express');
var exphbs = require('express-handlebars');
var router = express.Router();
var bodyParser = require('body-parser');
var mywebhook = require('./routes/mywebhook');
var app = express();
.
.
.
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json()); // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use('/callwebhook', mywebhook);
.
.
.
so on
Pretty sure I am missing something or doing something wrong, but I am not able to figure it out.
Thanks.
I finally found what was going on.
The way the body-parser works is that it will only try to parse a request in which they understand the Content-Type. This is mainly so you can stack them (app.use multiple parser types without conflict) and also because you typically don't want to parse a request that's going to fail (Content-Type: text/html is unlikely to ever get through a JSON.parse, for example).
I ended up getting sent */*; charset=UTF-8 which is not even a valid Content-Type header value period. The body-parser module refused to accept it, since that is gibberish. This module does allow you to setup a function that lets you put any custom logic you want to perform the filtering.
I had to put the body parser in my router code just for this webhook case.
var bodyParser = require('body-parser');
var customParser = bodyParser.json({type: function(req) {
return req.headers['content-type'] === '*/*; charset=UTF-8';
}});
router.post('/success', customParser, function(req, res){
console.log(JSON.stringify(req.body));
});
#svens thank you for your help.

Attempting to send object from client to server (AngularJS $http.post)

I'm attempting to store an object that my user clicks on in my server so that when the page changes, all the information from that object can be displayed fully in a profile page.
I'm unfamiliar with Angular $http but I've tried to write a call that will POST to the server, unfortunately when I scan through the req object in VScode I can't find where the object I sent is contained, so I can send it on to my function.
Controller function:
$scope.storeProfile = function(child){
$http.post('/storeTempProfile', child)
.then(function(response) {
window.location.href = 'DemoPage.html';
});
}
server.js:
app.post('/storeTempProfile', function (req, res) {
profileStorage.storeProfile(req);
});
does my app.post look right? And what property of req do I need to use the dot operator on to access my object? I can't seem to find the object data anywhere in req and that makes me thing there's something wrong with how I wrote app.post
It looks like you are using express. So in that case, you want to access the object on req.body, but this will require you use body-parser. The example on their homepage:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// create application/json parser
var jsonParser = bodyParser.json()
// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
if (!req.body) return res.sendStatus(400)
// create user in req.body
})
You will notice in this example that they pass the json parser into the route itself. This is only necessary if you want to have different parsers for different routes. Usually you just want to set it to all routes, which you can do by using app.use(bodyParser.json()).

reading request object in node.js from localhost

I'm new to node.js and I'm trying out a few easy examples using localhost:XXXX.
I want to read my request object from node. I have a book and in the book they use cURL(some program) to comunicate with node instead of the browser. Is it possible to write something in the browser adress field and send it to localhost and have a request object sent to node that looks exactly like if I had a typed in a url to a server somewhere? OIf so, how do I write? Do I have to use cURL or something like it if i use localhost?
I'm very new to node and javascript so I dont't know if I'm using the right words. I have tried to search but I dont't think I know the right terms to search for.
This is my server code:
var port = 3000;
http.createServer(function (req, res) {
var url = parse(req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n' + url );
}).listen(port);
When i write http://localhost:3000/hello.com in the address field i was hoping i would get Hello world hello.com in the browser but i get hello world [object object]
Please help.
You can use your regular browser by testing it. In your URL address enter URL address that you have in your cURL address. For instance:
localhost:3000/index.html
If you would like to have more sophisticated tool that gives you more information about request/response you can use tool like Postman for that
In your code use:
res.end('Hello World\n' + url.parse(req.url, true));
url is an object, you need to specify property or method that you are calling on it.
Here is an example on how to parse URL. Easy URL Parsing With Isomorphic JavaScript:
Above answer given by #Vlad Beden looks good but you may want to play with following code
var http = require("http");
var port = 3000;
http.createServer(function (req, res) {
console.log('Requested method: ', req.method);
var params = parseUrl(req.url);
res.writeHead(200, { 'Content-Type': 'text/plain' });
var data = 'Hello World'
for(var i=0; i<params.length; i++)
data += '\n'+params[i]
res.end(data);
}).listen(port);
var parseUrl = function(url) {
var params = [];
if(url && url != '' && url != '/') {
url = url.replace(/^\/|\/$/g, '');
params = url.split('/');
}
return params;
}
You may try http://localhost:3000/hello.com or http://localhost:3000/hello.com/google.com/more.com/etc . I would like to recommend you print request object console.log(req) and have a look to understand url, method, headers, etc.

How to disable Express BodyParser for file uploads (Node.js)

This seems like it should be a fairly simple question, but I'm having a really hard time figuring out how to approach it.
I'm using Node.js + Express to build a web application, and I find the connect BodyParser that express exposes to be very useful in most cases. However, I would like to have more granular access to multipart form-data POSTS as they come - I need to pipe the input stream to another server, and want to avoid downloading the whole file first.
Because I'm using the Express BodyParser, however, all file uploads are parsed automatically and uploaded and available using "request.files" before they ever get to any of my functions.
Is there a way for me to disable the BodyParser for multipart formdata posts without disabling it for everything else?
If you need to use the functionality provided by express.bodyParser but you want to disable it for multipart/form-data, the trick is to not use express.bodyParser directly. express.bodyParser is a convenience method that wraps three other methods: express.json, express.urlencoded, and express.multipart.
So instead of saying
app.use(express.bodyParser())
you just need to say
app.use(express.json())
.use(express.urlencoded())
This gives you all the benefits of the bodyparser for most data while allowing you to handle formdata uploads independently.
Edit: json and urlencoded are now no longer bundled with Express. They are provided by the separate body-parser module and you now use them as follows:
bodyParser = require("body-parser")
app.use(bodyParser.json())
.use(bodyParser.urlencoded())
If the need for body parsing depends only on the route itself, the simplest thing is to use bodyParser as a route middleware function on only the routes that need it rather than using it app-wide:
var express=require('express');
var app=express.createServer();
app.post('/body', express.bodyParser(), function(req, res) {
res.send(typeof(req.body), {'Content-Type': 'text/plain'});
});
app.post('/nobody', function(req, res) {
res.send(typeof(req.body), {'Content-Type': 'text/plain'});
});
app.listen(2484);
When you type app.use(express.bodyParser()), almost each request will go through bodyParser functions (which one will be executed depends on Content-Type header).
By default, there are 3 headers supported (AFAIR). You could see sources to be sure. You can (re)define handlers for Content-Types with something like this:
var express = require('express');
var bodyParser = express.bodyParser;
// redefine handler for Content-Type: multipart/form-data
bodyParser.parse('multipart/form-data') = function(req, options, next) {
// parse request body your way; example of such action:
// https://github.com/senchalabs/connect/blob/master/lib/middleware/multipart.js
// for your needs it will probably be this:
next();
}
upd.
Things have changed in Express 3, so I'm sharing updated code from working project (should be app.useed before express.bodyParser()):
var connectUtils = require('express/node_modules/connect/lib/utils');
/**
* Parses body and puts it to `request.rawBody`.
* #param {Array|String} contentTypes Value(s) of Content-Type header for which
parser will be applied.
* #return {Function} Express Middleware
*/
module.exports = function(contentTypes) {
contentTypes = Array.isArray(contentTypes) ? contentTypes
: [contentTypes];
return function (req, res, next) {
if (req._body)
return next();
req.body = req.body || {};
if (!connectUtils.hasBody(req))
return next();
if (-1 === contentTypes.indexOf(req.header('content-type')))
return next();
req.setEncoding('utf8'); // Reconsider this line!
req._body = true; // Mark as parsed for other body parsers.
req.rawBody = '';
req.on('data', function (chunk) {
req.rawBody += chunk;
});
req.on('end', next);
};
};
And some pseudo-code, regarding original question:
function disableParserForContentType(req, res, next) {
if (req.contentType in options.contentTypes) {
req._body = true;
next();
}
}
Within Express 3, you can pass parameter to the bodyParser as {defer: true} - which in term defers multipart processing and exposes the Formidable form object as req.form. Meaning your code can be:
...
app.use(express.bodyParser({defer: true}));
...
// your upload handling request
app.post('/upload', function(req, res)) {
var incomingForm = req.form // it is Formidable form object
incomingForm.on('error', function(err){
console.log(error); //handle the error
})
incomingForm.on('fileBegin', function(name, file){
// do your things here when upload starts
})
incomingForm.on('end', function(){
// do stuff after file upload
});
// Main entry for parsing the files
// needed to start Formidables activity
incomingForm.parse(req, function(err, fields, files){
})
}
For more detailed formidable event handling refer to https://github.com/felixge/node-formidable
I've faced similar problems in 3.1.1 and found (not so pretty IMO) solution:
to disable bodyParser for multipart/form-data:
var bodyParser = express.bodyParser();
app.use(function(req,res,next){
if(req.get('content-type').indexOf('multipart/form-data') === 0)return next();
bodyParser(req,res,next);
});
and for parsing the content:
app.all('/:token?/:collection',function(req,res,next){
if(req.get('content-type').indexOf('multipart/form-data') !== 0)return next();
if(req.method != 'POST' && req.method != 'PUT')return next();
//...use your custom code here
});
for example I'm using node-multiparty where the custom code should look like this:
var form = new multiparty.Form();
form.on('file',function(name,file){
//...per file event handling
});
form.parse(req, function(err, fields, files) {
//...next();
});
With express v4, and body-parser v1.17 and above,
You can pass a function in the type of bodyParser.json.
body-parser will parse only those inputs where this function returns a truthy value.
app.use(bodyParser.json({
type: function(req) {
return req.get('content-type').indexOf('multipart/form-data') !== 0;
},
}));
In the above code,
the function returns a falsy value if the content-type is multipart/form-data.
So, it does not parse the data when the content-type is multipart/form-data.
throw this is before app.configure
delete express.bodyParser.parse['multipart/form-data'];

Nodejs Expressjs URI characters

Is there a way to restrict allowed uri characters in Expressjs. like set a variable of characters:
allowed_uri_chars = 'a-zA-Z0-9'; etc
The most logical and easiest way to go would be to use a regex in some middleware:
var url = require("url");
app.use(function(req, res, next) {
var pathname = url.parse(req.url).pathname;
if(!pathname.match(/^[a-z0-9\/]+$/i)) return res.send(403);
next();
});
Or, as the URL is already parsed by Express, but this is not exposed publicly, you may want to save one call to parse and rely on Express internals (which I wouldn't advise):
app.use(function(req, res, next) {
if(!req._parsedUrl.pathname.match(/^[a-z0-9\/]+$/i)) return res.send(403);
next();
});
This ensures that your pathname (for http://example.com/foo/bar?id=1334, pathname would be /foo/bar) only contains letters, numbers, and slashes. It raises a 403 if not.
Can't you use a regex to parse the authorized chars?
Or do you mean in the routing directly?

Categories