Jesse
Jesse

Reputation: 10466

Express.js routing: optional splat param?

I have a route that looks like this:

app.all('/path/:namedParam/*splat?',function(req,res,next){
  if(!req.params.length){
    // do something when there is no splat
  } else {
    // do something with splat
  }
});

however, this doesn't work - if I call path/foo/bar it hits the route, but if I call path/foo, it doesn't.

Is it possible to have an optional splat param, or do I have to use a regex to detect this?

Edit:

to be clearer, here are the requirements I'm trying to achieve:

Upvotes: 88

Views: 80391

Answers (8)

Koen.
Koen.

Reputation: 26959

To have any trailing path to end up in a named param you can add parentheses to the asterisk;

router.get('/path/:trailing(*)?', function(req, res, next) {
  console.log(req.params.trailing);
  // ...
});

This for /path/level1 would have the trailing param set to level1, and for /path/level1/level2/level3 to level1/level2/level3.

Upvotes: 1

Andreas Hultgren
Andreas Hultgren

Reputation: 14953

I just had the same problem and solved it. This is what I used:

 app.get('path/:required/:optional*?', ...)

This should work for path/meow, path/meow/voof, path/meow/voof/moo/etc...

It seems by dropping the / between ? and *, the last / becomes optional too while :optional? remains optional.

Upvotes: 77

yaya
yaya

Reputation: 8243

Suppose you have this url: /api/readFile/c:/a/a.txt

If you want req.params.path to be c::

'/api/readFile/:path*

If you want req.params.path to be c:/a/a.txt:

'/api/readFile/:path([^/]*)'

Upvotes: 7

williamli
williamli

Reputation: 4104

I got around this problem by using a combination of a middleware that add trailing slashes to url and router.get('/bar/*?', ..., which will pick up everything after /bar/, and return undefined if it is just /bar/. If the visitor asked for /bar, the express-slash middleware will add a slash to the request and turns the request into /bar/.

Upvotes: 0

Chris
Chris

Reputation: 1829

This works for /path and /path/foo on express 4, note the * before ?.

router.get('/path/:id*?', function(req, res, next) {
    res.render('page', { title: req.params.id });
});

Upvotes: 115

Dave Ward
Dave Ward

Reputation: 60580

Will this do what you're after?

app.all('/path/:namedParam/:optionalParam?',function(req,res,next){
  if(!req.params.optionalParam){
    // do something when there is no optionalParam
  } else {
    // do something with optionalParam
  }
});

More on Express' routing here, if you haven't looked: http://expressjs.com/guide/routing.html

Upvotes: 18

windmaomao
windmaomao

Reputation: 7671

The above solutions using optional doesn't work in Express 4. And I tried couple of ways using search patterns, but not working either. And then I found this method and seems fired for unlimited nested path, http://expressjs.com/api.html#router

// this will only be invoked if the path starts with /bar from the mount point
router.use('/bar', function(req, res, next) {
  // ... maybe some additional /bar logging ...

  // to get the url after bar, you can try
  var filepath = req.originalUrl.replace(req.baseUrl, "");

  next();
});

It's match all /bar, /bar/z, /bar/a/b/c etc. And after that, you can read req.originalUrl, since params are not filled, ex. you can try compare baseUrl and originalUrl to get the remaining path.

Upvotes: 0

Jesse
Jesse

Reputation: 10466

Here's the current way I'm solving this problem, it doesn't appear that express supports any number of splat params with an optional named param:

app.all(/\/path\/([^\/]+)\/?(.+)?/,function(req,res,next){
  // Note: this is all hacked together because express does not appear to support optional splats.
  var params = req.params[1] ? [req.params[1]] : [],
      name = req.params[0];
  if(!params.length){
    // do something when there is no splat
  } else {
    // do something with splat
  }
});

I'd love to have this use named params for readability and consistency - if another answer surfaces that allows this I'll accept it.

Upvotes: 3

Related Questions