Express.js URL encoding of parameters w/ special characters - node.js

I am building an express.js web API and have hit a snag working with our legacy data. We have identity fields on a particular data model that contains lots of different special characters. Here are some of our example ids:
W2220-059AP0254LL%M031
SMT II #12B75984
ST14P134-0110072,9\'98
TKH-12-110?????
So we have an endpoint like: /thing/{id}
Obviously the above examples don't work with this endpoint. I have tried to do URI encoding in our first entrypoint with express but by the time it hits that it is already too late in most cases.
Is there any other way to achieve this?

Try this middleware (before any of your routes):
app.use(function(req, res, next) {
req.url = req.url.replace(/^(\/thing\/)(.+)/, function($0, $1, $2) {
return $1 + encodeURIComponent($2);
});
next();
});
This assumes that all requests to /thing/:id are not properly encoded.

Related

How to ignore specific files to be loaded when I use route parameters in Express

When I make a GET request with route parameters in express with mongoose like the following code, I sometimes see that the browser tries to load some unexpected files such as favicon.ico, robots.txt, humans.txt, sitemap.xml, ads.txt, etc., and 404 error shows up in the browser console.
app.get("/:userId", ...);
By refering to this Q&A, I figured out that if I don't use the route parameters right after the root route like the following code, it doesn't happen.
app.get("/user/:userId", ...);
In the same Q&A, however, there seem to be another way that uses req.url to ignore those unexpected files to be loaded, but it isn't explained in detail.
How do you do that?
All that's meant in that other answer is that you could examine req.url in your route handler and make sure it is not a known special name. In this specific case, it's probably simpler to use req.params.userId instead of req.url, but you could also use req.url in the same way.
const specials = new Set(["favicon.ico", "robots.txt", "humans.txt", "sitemap.xml", "ads.txt"]);
app.get("/:userId", (res, res, next) => {
// if it's a special URL, then skip it here
if (specials.has(req.params.userId)) {
next();
return;
}
// process your route here
});
Personally, I wouldn't recommend this solution because it presupposes a perfect knowledge of all possible special filenames. I don't use a top level wildcards ever because they ruin the ability to use your server for anything else.

URL parameters before the domain

I have a question about routing and urls in general. My question regards parameters or queries in the url before the domain itself. For example:
http://param.example.com/
I ask this question because I am using ExpressJS and you only define your routes after the domain itself like:
http://example.com/yourRoute
Which would be added to the end of the url. What I want is to be able to store parameters inbefore the domain itself.
Thank you in advance!
FYI
I do know how to use parameters and queries, I just don't know how I would go about to insert them before the domain.
You can create an if statement which can look at the sub-domain through the express req.headers.host variable which contains the domain of the request. For example:
-- google.com/anything/another
req.headers.host => "google.com"
-- docs.google.com/anything/
req.headers.host => "docs.google.com"
So working off this in your route you can call Next() if the request doesn't match the form you want.
router.get('/', function(req, res, next) {
if (req.headers.host == "sub.google.com") {
//Code for res goes here
} else {
//Moves on to next route option b/c it didn't match
next();
}
});
This can be expanded on a lot! Including the fact that many packages have been created to accomplish this (eg. subdomain) Disclaimer you may need to account for the use of www. with some urls.
Maybe this vhost middleware is useful for your situation: https://github.com/expressjs/vhost#using-with-connect-for-user-subdomains
Otherwise a similar approach would work: create a middleware function that parses the url and stores the extracted value in an attribute of the request.
So I would use something like
router.get('/myRoute', function(req, res,next) {
req.headers.host == ":param.localhost.com"
//rest of code
}
I think I understand what you are saying, but I will do some testing and some further reading upon the headers of my request.
EDIT: Right now it seems like an unnecessary hassle to continue with this because I am also working with React-router at the moment. So for the time being I am just going to use my params after the /.
I thank you for your time and your answers. Have a nice day!

REST method GET for searching by criteria as json fro mongodb

I have an nodejs server with express and mongoose and I'd like to use method GET for search accoring to criteria which I'd like to provide as JSON object does anyone can help me how to implement it?
Or maybe should I use POST or PUT to make it?
http://hostname/modelname?criteria1=1&criteria2=2
router.route('/modelname')
.get(function (req, res, next) {
req.query.criteria1 === 1 // true
req.query.criteria2 === 2 // true
});
If you are unsure of what HTTP VERB you'd want to use,
POST - is primarily used for creating resources on the server
PUT - is used to update an existing resource
GET- to retrieve a resource
I would use GET in this case
GET http://myhost.com?q=word1+word2+word3+word4
app.get('*', function(req, res) {
// do something with the req.query.q array
});
You've got two options - using HTTP GET params or encoding whole criteria as JSON string and passing that string.
First option, by using params:
?crit[foo.bar][$eq]=abc&crit[foo.baz][$ne]=def
You can read it in nodejs/express via req.query.crit. But this is bad idea because there's no way of retaining data types. For example number 1 becomes string "1". Don't use it - MongoDB is data type sensitive so query {"foo.bar":{$eq:1}} is completely different from {"foo.bar":{$eq:"1"}}
Second option is to urlencode JSON criteria:
?critJSON=%7B%22foo.bar%22%3A%7B%22%24eq%22%3A%20%22abc%22%7D%2C%22foo.baz%22%3A%7B%22%24ne%22%3A%20%22def%22%7D%7D
And parse it on nodejs/express side:
var crit = JSON.parse(req.query.critJSON)
This way your data types are retained and it will work as expected.

How to get optional language parameter from URL in Express routes?

I'm stuck with a stupid problem:
How to work with optional locale parameter?
That's what I mean:
For example, I have frontpage and contacts, here is routes:
app.get('/', frontpage.get);
app.get('/contacts', contacts.get);
Now I'm trying to add localization to my site
app.all('/:lang?*', language.all); -> detect and set locale
app.get('/:lang?', frontpage.get);
app.get('/:lang?/contacts', contacts.get);
The only problem is when I don't use lang-parameter in URL:
mysite.com/contacts
because Express uses 'contacts' as a language parameter.
(+ I don't like this copy-pasted :lang?)
I think, I just took the wrong way.
How to use locale parameter from URL in Express?
PS: I dont want to use subdomains de.mysite.com or querystring mysite.com?lang=de. I want exactly this variant mysite.com/de
Most modules use Accept-Language so I can't find any that use the path like this that you might be able to use. So you'll need to define your own middleware that initializes before everything else. Express's Router doesn't really help for your usecase.
app.use(function(req, res, next){
var match = req.url.match(/^\/([A-Z]{2})([\/\?].*)?$/i);
if (match){
req.lang = match[1];
req.url = match[2] || '/';
}
next();
});
Now you can use req.lang in your routes or other middleware to configure your translation logic and since we have rewritten the URL, later logic will not know that there is a language param.

Express Framework app.post and app.get

I am fairly new to the express framework. I couldn't find the documentation for application.post() method in the express API reference. Can someone provide a few examples of all the possible parameters I can put in the function? I've read a couple sites with the following example, what does the first parameter mean?
I know the second parameter is the callback function, but what exactly do we put in the first parameter?
app.post('/', function(req, res){
Also, let's say we want the users to post(send data to our server) ID numbers with a certain format([{id:134123, url:www.qwer.com},{id:131211,url:www.asdf.com}]). We then want to extract the ID's and retrieves the data with those ID's from somewhere in our server. How would we write the app.post method that allows us to manipulate the input of an array of objects, so that we only use those object's ID(key) to retrieve the necessary info regardless of other keys in the objects. Given the description of the task, do we have to use app.get() method? If so, how would we write the app.get() function?
Thanks a lot for your inputs.
1. app.get('/', function(req, res){
This is telling express to listen for requests to / and run the function when it sees one.
The first argument is a pattern to match. Sometimes a literal URL fragment like '/' or '/privacy', you can also do substitutions as shown below. You can also match regexes if necessary as described here.
All the internal parts of Express follow the function(req, res, next) pattern. An incoming request starts at the top of the middleware chain (e.g. bodyParser) and gets passed along until something sends a response, or express gets to the end of the chain and 404's.
You usually put your app.router at the bottom of the chain. Once Express gets there it starts matching the request against all the app.get('path'..., app.post('path'... etc, in the order which they were set up.
Variable substitution:
// this would match:
// /questions/18087696/express-framework-app-post-and-app-get
app.get('/questions/:id/:slug', function(req, res, next){
db.fetch(req.params.id, function(err, question){
console.log('Fetched question: '+req.params.slug');
res.locals.question = question;
res.render('question-view');
});
});
next():
If you defined your handling functions as function(req, res, next){} you can call next() to yield, passing the request back into the middleware chain. You might do this for e.g. a catchall route:
app.all('*', function(req, res, next){
if(req.secure !== true) {
res.redirect('https://'+req.host+req.originalUrl);
} else {
next();
};
});
Again, order matters, you'll have to put this above the other routing functions if you want it to run before those.
I haven't POSTed json before but #PeterLyon's solution looks fine to me for that.
TJ annoyingly documents this as app.VERB(path, [callback...], callback in the express docs, so search the express docs for that. I'm not going to copy/paste them here. It's his unfriendly way of saying that app.get, app.post, app.put, etc all have the same function signature, and there are one of these methods for each supported method from HTTP.
To get your posted JSON data, use the bodyParser middleware:
app.post('/yourPath', express.bodyParser(), function (req, res) {
//req.body is your array of objects now:
// [{id:134123, url:'www.qwer.com'},{id:131211,url:'www.asdf.com'}]
});

Resources