Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the Express route param regex `(*)` mean?

I cut my teeth on Perl. I'm pretty comfortable with regular expressions (but still prone to errors).

Why does (*) work as a regular expression in an Express route named param?

Why doesn't (.*) work as a regular expression in an Express route named param?

Is something like ([\\w:./]+) a more reliable way to do it?


I'm trying to use a route parameter that is intended to have slashes in the value.

e.g.

If the request is:

http://www.example.com/new/https://www.youtube.com/trending

... and I'm using this route:

app.get('/new/:url', (req, res) => {
  console.log('new')
  console.log(req.params.url)
})

I want url to equal https://www.youtube.com/trending

I understand that the path is split on the slashes, so I thought I could use a regular expression in parentheses after the named parameter to also match the slashes.

I tried /new/:url(.*), which I thought should greedily match anything, including the slashes, but this made the route fail completely. Why doesn't this work?

Through my own trial and error, I found that /new/:url([\\w:./]+) works. This makes sense to me, but seems unnecessarily complex. Is this "the right way"?

The one that perplexes me the most is one I found in a YouTube video example... Why does /new/:url(*) work? The * says 0 or more of the previous item, but there's nothing before the asterisk.

I have a feeling that the answer lies in this GitHub issue, but it's not clear to me from reading the thread exactly what's happening. Does (*) rely on a bug that's likely to be corrected in the next release of Express?

like image 460
Vince Avatar asked Mar 19 '18 09:03

Vince


People also ask

What is router Param in Express?

param(name, callback) adds a callback to the route parameters where name defines the name of the parameter and callback is the callback function. Following are the parameters of the callback function − req – the request object.

What are Express params?

In Express, route parameters are essentially variables derived from named sections of the URL. Express captures the value in the named section and stores it in the req. params property. const app = require('express')(); // `:userId` is a route parameter.

What is Express routing?

Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (GET, POST, and so on). Each route can have one or more handler functions, which are executed when the route is matched.

How use regex in Express js?

We can easily set up Regex for our Express Router by following these two points: To make our route match with a particular regular expression, we can put regex between two forward slashes like this /<routeRegex>/ Since every route contain /, so wherever we have to use / inside Regex, use a backslash \ / before it.


1 Answers

The first part of the question is answered by the referenced GitHub issue.

As for why .* doesn't work, the dot (.) isn't a special character in this implementation. It's just a dot.

From the referenced GitHub issue I understand that the asterisk (*) isn't understood as a quantifier at all. It just matches everything. So, that's why (*) works.

The part that isn't explained by the GitHub issue is .* which, when taking the known bug into consideration, should match a single character followed by everything else. However, through trial and error, I've determined that the . isn't a special character at all. In this implementation, it's just a literal dot.

For example, if the request is:

http://www.example.com/new/https://www.youtube.com/trending

... and I'm using this route:

app.get('/new/:url(.*)', (req, res) => {
  console.log('new')
  console.log(req.params.url)
})

The route wouldn't be matched, but a request for

http://www.example.com/new/.https://www.youtube.com/trending

would match (note the dot preceding the https) and req.params.url would equal .https://www.youtube.com/trending.

I used the following code to test:

const express = require('express')
const app = express()
const port = process.env.PORT || 3000

app.get('/dotStar/:dotStar(.*)', (request, response) => {
  console.log(`test request, dotStar: ${request.params.dotStar}`)
  response.send(`dotStar: ${request.params.dotStar}`)
})

app.get('/star/:star(*)', (request, response) => {
  console.log(`test request, star: ${request.params.star}`)
  response.send(`star: ${request.params.star}`)
})

app.get('/regexStar/:regexStar([a-z][a-z-]*)', (request, response) => {
  console.log(`test request, regexStar: ${request.params.regexStar}`)
  response.send(`regexStar: ${request.params.regexStar}`)
})

app.get('/dotDotPlus/:dotDotPlus(..+)', (request, response) => {
  console.log(`test request, dotDotPlus: ${request.params.regexStar}`)
  response.send(`dotDotPlus: ${request.params.dotDotPlus}`)
})

app.get('/regex/:regex([\\w:./-]+)', (request, response) => {
  console.log(`test request, regex: ${request.params.regex}`)
  response.send(`regex: ${request.params.regex}`)
})

app.listen(port, () => {
  console.log(`Listening on port ${port}...`)
})

-- Also found in this gist

like image 148
Vince Avatar answered Oct 12 '22 09:10

Vince