Pierre Maoui
Pierre Maoui

Reputation: 6384

NodeJS/Express How to create 2 routes, similar path but different kind of parameters?

I try to create two distinct routes:

  1. /path/1234 (4 digits or less)
  2. /path/123456789 (more than 4)

What I'm trying :

app.param(function (name) {
  return function(req,res,next,id) {
    //do some stuff here with id
    next();
  };
});
app.param('shortId', /[0-9]{0,4}/);
app.param('longId', /[0-9]{5,20}/);

app.get('/path/:shortId', function(req, res){
  res.send('ShortID:' + req.params.shortId);
});

app.get('/path/:longId', function(req, res){
  res.send('LongId:' + req.params.longId);
});

I always obtain a hit from the first path (shortId) even with a number longer than 4 digits. Do someone has an idea of what I'm doing incorrectly here ?

Upvotes: 2

Views: 1566

Answers (3)

Pierre Maoui
Pierre Maoui

Reputation: 6384

It seems that Express match the first expression partially. It is not intuitive, but it still possible to obtain the wanted result by checking "manually" :

app.param(function (name,fn) {
  if (fn instanceof RegExp) {

    //fn is the regexp that I can use here
    return function(req,res,next,id) {

      if ( String(id).match(fn) ) {
        //do some stuff here with id
        next();
      }
      else {
        next('route');
      }
    };
  }
});

As said by bduran, the correct regexps are :

app.param('shortId', /^[0-9]{0,4}$/);
app.param('longId', /^[0-9]{5,20}$/);

Upvotes: 1

Joachim Isaksson
Joachim Isaksson

Reputation: 180927

You don't really need express-params for this, you can just append a parenthesized regex directly to the parameter;

app.get('/path/:shortId([0-9]{0,4})', function(req, res){
  res.send('ShortID:' + req.params.shortId);
});

app.get('/path/:longId([0-9]{5,20})', function(req, res){
  res.send('LongId:' + req.params.longId);
});

EDIT:

To make express-params work, there are two things that cause problems in your existing example.

  • As others have pointed out, you'll need to anchor your regular expressions using /^...$/, or the first expression will partially match the parameter and always be taken.

  • Your first app.param call (where you pass the function) always returns a function which means overriding the other app.param calls to use the function for matching instead of the regex. Since the function always calls the parameterless next(), it always accepts all patterns.

Changing that would result in something like;

app.param(function (name, param) {
  if(name != 'veryshortid')           // Use this function for veryshortid only
    return;
  return function(req,res,next,id) {
    if(id.length > param) next('route');       // No match, skip
    else next();                               // Accept
  };
});

app.param('veryshortid', 1);
app.param('shortid', /^[0-9]{2,4}$/);
app.param('longid', /^[0-9]{5,20}$/);

app.get('/path/:veryshortid', function(req, res, next){
  res.send('VeryShortID:' + req.params.veryshortid);
});

app.get('/path/:shortid', function(req, res, next){
  res.send('ShortID:' + req.params.shortid);
});

app.get('/path/:longid', function(req, res, next){
  res.send('LongId:' + req.params.longid);
});

Upvotes: 3

durum
durum

Reputation: 3404

It's because in Express logic the first regular expression match the longId, although partially. Be more restrictive and everything will go fine:

  app.param('shortId', /^[0-9]{0,4}$/);
  app.param('longId', /^[0-9]{5,20}$/);

Edited:

Also you can change the order in what you load the routes, but it's messy and cheap. Be restrictive ever, marking the beginning and the end of the regular expressions.

Upvotes: 1

Related Questions