Giter Site home page Giter Site logo

routes.js's Introduction

Routes.js

routes lets you easily dispatch based on url-style strings. It comes with a default Router function that you can use to route http requests, but it also cleanly exposes the important functionality so you could also use it to perform more generic string pattern matching.

This might make it useful for things like:

  • URI routing
  • Cucumber-style pattern matching :)
  • Routing messages by channel name from an MQ
  • Dispatching hierarchical events by name

Alternative routers.

This module is no longer actively worked on. This module operates just fine as is, however you might want to use a module that is under active maintenance:

Router Example:

The full range of Path Formats is documented below.

var Router = require('routes');
var router = Router();
var noop = function(){};

router.addRoute("/articles/:title?", noop);
router.addRoute("/:controller/:action/:id.:format?", noop);

console.log(router.match("/articles"));
console.log(router.match("/articles/never-gonna-let-you-down"));
console.log(router.match("/posts/show/1.json"));

The output for router.match("/posts/show/1.json") would be:

{
  params: {
    controller: 'posts',
    action: 'show',
    id: '1',
    format: 'json'
  },
  splats: [],
  route: '/:controller/:action/:id.:format?',
  fn: [Function],
  next: [Function]
}

In the example above, fn would be the function that was passed into the router.

I return this object instead of calling your function for you because you will likely want to add additional parameters from the current context to the function invocation. Ex:

var route = router.match("/posts/show/1.json");
route.fn.apply(null, [req, res, route.params, route.splats]);

HTTP Method Example:

Here is a handy trick if you want to perform pattern matching on http methods:

var router = require('routes')();

router.addRoute("GET /articles/:title?", function (req, res, params) {
  // perform some IO...
  res.end('article content goes here...\n');
});
router.addRoute("POST /articles/:title", function (req, res, params) {
  // perform some IO...
  res.setHeader('content-type', 'text/plain');
  res.end('updated ' + params.title + '\n');
});

var http = require('http');
var server = http.createServer(function (req, res) {
 var m = router.match(req.method + ' ' + req.url);
 if (m) m.fn(req, res, m.params);
 else {
  res.statusCode = 404;
  res.end('not found\n');
 }
});
server.listen(5000);

Match Continuation

The object returned by router.match includes a next function you can use to continue matching against subsequent routes. Routes are evaluated in the order they are added to the router, so generally, you would add your most specific routes first and most ambiguous routes last. Using the next function allows you evaluate more ambiguous routes first.

var Router = require('routes');
var router = new Router();

router.addRoute('/admin/*?', auth);
router.addRoute('/admin/users', adminUsers);

http.createServer(function (req, res) {
  var path = url.parse(req.url).pathname;
  var match = router.match(path);
  match.fn(req, res, match);
}).listen(1337)

// authenticate the user and pass them on to
// the next route, or respond with 403.
function auth(req, res, match) {
  if (checkUser(req)) {
    match = match.next();
    if (match) match.fn(req, res, match);
    return;
  }
  res.statusCode = 403;
  res.end()
}

// render the admin.users page
function adminUsers(req, res, match) {
  // send user list
  res.statusCode = 200;
  res.end();
}

Installation

npm install routes

Path Formats

Basic string:

"/articles" will only match routes that == "/articles".

Named parameters:

"/articles/:title" will only match routes like "/articles/hello", but *not* "/articles/".

Optional named parameters:

"/articles/:title?" will match "/articles/hello" AND "/articles/"

Periods before optional parameters are also optional:

"/:n.:f?" will match "/1" and "/1.json"

Splaaaat! :

"/assets/*" will match "/assets/blah/blah/blah.png" and "/assets/".

"/assets/*.*" will match "/assets/1/2/3.js" as splats: ["1/2/3", "js"]

Mix splat with named parameters:

"/account/:id/assets/*" will match "/account/2/assets/folder.png" as params: {id: 2}, splats:["folder.png"]

Named RegExp:

"/lang/:lang([a-z]{2})" will match "/lang/en" but not "/lang/12" or "/lang/eng"

Raw RegExp:

/^\/(\d{2,3}-\d{2,3}-\d{4})\.(\w*)$/ (note no quotes, this is a RegExp, not a string.) will match "/123-22-1234.json". Each match group will be an entry in splats: ["123-22-1234", "json"]

Router API

The Router() that routes exposes has two functions: addRoute and match.

addRoute: takes a path and a fn. Your path can match any of the formats in the "Path Formats" section.

match: takes a String or RegExp and returns an object that contains the named params, splats, route (string that was matched against), the fn handler you passed in with addRoute, and a next function which will run match against subsequent routes.

Library API

match: takes an array of Routes, and a String. It goes through Routes and returns an object for the first Route that matches the String, or undefined if none is found. The returned object contains params, splats, and route. params is an object containing the named matches, splats contains the unnamed globs ("*"), and route contains the original string that was matched against.

pathToRegExp: takes a path string and an empty keys array, returns a RegExp and populates keys with the names of the match groups that the RegExp will match. This is largely an internal function but is provided in case someone wants to make a nifty string -> [RegExp, keys] utility.

Test

Clone the repo, cd to it, and:

make test

Credits

This library is an extraction and re-factor of the connect router middleware. I found that connect-based routing worked reasonably well on the server side, but I wanted to do similar routing based on channel names when using Push-It and possibly for event names when using Evan. So, I extracted the relevant goodness out of the router middleware and presented it here. Big thanks to TJ Holowaychuk for writing the original router middleware.

License

This code is distributed under the MIT license, Copyright Aaron Blohowiak and TJ Holowaychuk 2011.

routes.js's People

Contributors

raynos avatar mikefrey avatar aaronblohowiak avatar elliotchong avatar phated avatar rikukissa avatar daniel-hug avatar esamattis avatar jeffbski avatar jfd avatar kemitchell avatar bevacqua avatar timoxley avatar

Stargazers

Dawadam avatar Jerry avatar  avatar Logan King (DarkComet) avatar Jan avatar Nikolai Minkevich avatar Tom Hummel avatar Rishikesh Darandale avatar Nitin Khanna avatar Jon Repp avatar Shu Mai avatar Cat  avatar vulcangz avatar  avatar  avatar Thomas avatar Guibo Li avatar  avatar TastyCarb avatar Dan DiGangi avatar iot_pro avatar takumi avatar Alessandro Gubitosi avatar  avatar Nyan avatar Denis Tokarev avatar Bran van der Meer avatar  avatar qwqcode avatar Christian Theilemann avatar Guilherme Pangnotta avatar Mikal avatar Philippe Laval avatar Brian Lee avatar Shohanul Alam avatar aci.vang avatar Tony Bradley avatar James Wiens avatar RAJESHW avatar Piotr Staniów avatar Jake Burden avatar Jesús Escamilla avatar deepskyblue avatar Alexander avatar Sergey Pomadin avatar Travis Taylor avatar Yuki Kodama avatar Li Jie avatar Alexander Gromnitsky avatar Lan Qingyong avatar Alon Valadji avatar Jakub Gawlas avatar Zava avatar Sergii Shymko avatar Yerko Palma avatar Risto Novik avatar Laz Bot avatar Batuhan Göksu avatar Jeremy Fairbank avatar Oliver.W avatar Denis avatar Lorin Hochstein avatar Jared Grippe avatar Joel Purra avatar Aissam BELHACHMI avatar Christoph Walcher avatar Gabriel S. Martins avatar Nico Lindemann avatar Martin Sandoval Carreon avatar Yanin avatar Fabien Franzen avatar Athan avatar salmon avatar Radu Micu avatar Abraham Itule avatar David Ed Mellum avatar Eric Dupertuis avatar Ben John avatar Philipp Otto avatar Krispin Schulz avatar Vinay M avatar Arun Michael Dsouza avatar Yoshiya Hinosawa avatar Jonathan Warning avatar  avatar Benjamim Sonntag avatar  avatar Marc Bachmann avatar 年糕小豆汤 avatar Yassine A. avatar vaibhav avatar Emil Bay avatar Rajika Imal avatar Denis Denisov avatar Rankin Zheng avatar Eder Ávila Prado avatar Jamie Curtis avatar Branden Pinney avatar Jim Kang avatar Yu-Shih Wang avatar

Watchers

 avatar Alberto Leal avatar Christian Hochfilzer avatar  avatar James Cloos avatar  avatar Shawn Jonnet avatar Shawn Jonnet avatar  avatar A.G. avatar Sam Tobia avatar  avatar Songyu Li avatar Batuhan Göksu avatar  avatar mistwan avatar

routes.js's Issues

Optional/strict trailing slash

The current documentation is somewhat misleading with regards to the trailing slash:

"/articles" will only match routes that == "/articles".

suggests that trailing slash is rejected for this route, when in fact it is not.

My question is, is there a way to force no trailing slash, except of doing it outside of router.match?

Splats and params are being transposed

When running the below:

router = require("routes")();
router.addRoute("*example.com/:fooParam", function(){});
router.match("http://example.com/bar");

The result is:

{ params: { fooParam: 'http://' },
  splats: [ 'bar' ],
  route: '*example.com/:fooParam',
  next: [Function],
  fn: [Function] }

Expected result:

{ params: { fooParam: 'bar' },
  splats: [ 'http://' ],
  route: '*example.com/:fooParam',
  next: [Function],
  fn: [Function] }

When additional splats and params are added (i.e.: *://*.example.com/:foo/:bar/:baz) the first n (in this case, 2) splats / params will be transposed.

Splats don't match empty string

The README claims that the route /assets/* matches /assets/. This does not appear to be true.

The generated regexp to replaces the * with (.+) rather than (.*).

I'm fairly certain this is intentional, but it means users will have to define an additional route when the behaviour described in the README is desired.

Offer Named Routes and/or Expose routes

I'm working with this library and am trying do good testing it on it and need to expose functions in the library itself.

having something like

router.routes ->
{
'^$': view,
'.': view
}

Or something like 

addRoute(...., name='name')
getRoute(name='name')

routes with N splats

hey, suppose that I want to parse some thing like this:

"/aaa%2Fbb%2Fcc/uuuuu"

if I use a route like: "/*" i get:

{ params: {},
  splats: [ 'aaa/bb/cc/uuuuu' ],
  route: '/*',
  fn: [Function] }

I was kinda expecting

{ params: {},
  splats: [ 'aaa/bb/cc', 'uuuuu' ],
  route: '/*',
  fn: [Function] }

what do you think, is this reasonable behavior?

and can you point me to where I might start fixing this?

by the way, I want to use this to parse CLI commands. i'm passing them through:

argv._.map(function (u) {
  return '/'+ encodeURIComponent (u)
}).join('')

Request: optional params a la Slim

Slim (PHP) has a nice way to let you describe optional params:

/my/route(/:optional)

It would be great, and very useful in many cases, to have that feature in routes.js.

matching characters like "

is it possible to do this? my use case maybe be a bit non-traditional, but I'm using routes in an IRC bot. it's probably the easiest way of parsing input I've ever encountered to be honest. An example route is as follows:

!add event yesno ":question" ":affirm" ":deny"

for example.

Request: Parsing querystring

I would find it very useful if routes.js returned the qs (what comes after the "?" for a given route passed to match()) in the params.

If you guys ever feel like beating me to it...

publish 2.0.0 to NPM

It seems like 2.0.0 had been pushed to github but npm has the latest version as 1.2.0

Adding routes with the same path causes problems

In my code im doing something about the same as this

router.addRoute('samePath', function1);
rouder.addRoute('samePath', function2);

I expected to be able to use match continuation so that both functions ran like function1 function2. It looks like the same your handling the routeMap as a associated array
https://github.com/aaronblohowiak/routes.js/blob/master/index.js#L135
means that if something is added with the same path the 2nd one will override the first and also be run twice.
I don't think this is the desired(by you) behaviour. I believe the easiest solution is to add the router to a list like [[path, function], [path, function]] and use the numeric index to look them back up.

Query string is blindly sucked into route parameter parsing

Do

routes('/articles/feed-the-pony?foo=3');

Get

{
  "params": {
    "slug": "feed-the-pony?foo=3",
    "args": []
  },
  "splats": [],
  "route": "/articles/:slug"
}

Expect something akin to:

{
  "params": {
    "slug": "feed-the-pony",
    "args": ["?foo=3"]
  },
  "splats": [],
  "route": "/articles/:slug"
}

Maybe add query string parameters as a new property in params. Or even ignore the ?foo=3 part entirely.

Same issue with hash parts, but I would expect those to be entirely ignored. e.g not show up anywhere.

Global search in regex

There's still actually one problem with this one. For example if I have a refex /a/g and the path we are matching it against is aaa then the splats params should be ["a", "a", "a"].

Now the match function actually starts looping through captures from the second element so the first match is omitted.

for (var j = 1, len = captures.length; j < len; ++j) {

So basically looping should start from the first element if the given regex is global.

Everything else would break if we just changed the looping start point.
One solution would do something like this

var j = re.global ? 0 : 1;
for (j, len = captures.length; j < len; ++j) {

Any thoughts on this? I'd be happy to create a new pull request with couple of new tests included and version number changed to 1.2.1.

PS. It's nice to see that you are responding to pull requests almost immediately!

`*` in named regular expressions is treated like a wildcard.

'/:test([1-9]\\d*)'
compiles to
/^\/(?:([1-9]\d(.*)))\/?$/i
but should compile to
/^\/(?:([1-9]\d*))\/?$/i
or to
/^\/(?:([1-9]\d{0,}))\/?$/i

Similar danger comes from using dots, groups and backreferences in named regular expressions, e.g. '/:test((.)\\1+)', but that might be much harder to resolve.

I suppose that it should be relatively easy to allow using dots in named RegExps, e.g. replace it for something like [\s\S], but right now I fail to find a legitimate use case for using dots. Asterisks however have much more potential, so I think it should be allowed to use them in named RegExps.

Matching routes with non-URI characters causes an exception

router.addRoute('*');
router.match('%');
URIError: URI malformed

Routes containing asterisk cause routes to throw an exception if router.match is called with characters like %.

My use case is to match random user input with routes, so this is a major problem.
Should this be fixed so that it would fail gracefully?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.