Provides Shelf middleware for defining routes.
shelf_route
is a powerful router that makes it easy to define routes in a modular way.
shelf_route
is designed to be:
This makes it very versatile and lets you mix and match it with your other favourite Shelf middleware components.
###Routing Choices###
There are a number of choices for routing in the shelf
world. Here is a simple guide to help you choose between a few of them.
shelf_route
. Good choice if:
you want a powerful router with a fluent api
you don't want to use mirrors or annotations
* you prefer a bit more boilerplate over any magic that comes with mirrorsshelf_route
plus
you are happy to use annotations (supported by mirrors) to significantly reduce boilerplate
* you like consistency in your REST APIs and like support to help with thatIn short, if you want to build your own stack then shelf_route
and shelf_rest
will likely suit you better. If you want a more fully featured framework, whilst still being highly extensible, then mojito
is the better option.
To get a good overview of the options you have, read the blog post [Routing Options in Shelf][routing_blog].
You create a router using the router
function
var myRouter = router();
Use the router
's get
method to add a route using the GET Http method
myRouter.get('/', (_) => new Response.ok("Hello World");
Use the router
's handler
property to obtain a Shelf Handler
var handler = myRouter.handler;
Now you can serve up your routes with Shelf IO
io.serve(handler, 'localhost', 8080);
So the complete hello world looks like
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_route/shelf_route.dart';
void main() {
var myRouter = router()
..get('/', (_) => new Response.ok("Hello World"));
io.serve(myRouter.handler, 'localhost', 8080);
}
It supports all the standard http methods
myRouter..get('/', (_) => new Response.ok("Hello World"))
..post('/', (_) => new Response.ok("Hello World"))
..put('/', (_) => new Response.ok("Hello World"))
..delete('/', (_) => new Response.ok("Hello World"));
You can specify several methods by using add
myRouter.add('/', ['GET', 'PUT'], (_) => new Response.ok("Hello World"));
Shelf Route uses UriPattern to define the paths to match on for each route. This means you can use whatever format for the paths that you like as long as it implements this interface.
By default it uses UriTemplate which implements the powerful standard of the same name.
UriTemplate allows binding to both:
path segments
like /greeting/fred**query parameters
like /greeting?name=fred**It uses {parameter name}
notation to denote path parameters.
myRouter.get('/{name}', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
Path parameters are fetched via Shelf Path's getPathParameter
function.
Similarly you can also bind to query parameters
myRouter.get('/{name}{?age}', myHandler);
myHandler(request) {
var name = getPathParameter(request, 'name');
var age = getPathParameter(request, 'age');
return new Response.ok("Hello $name of age $age");
}
To improve modularity you can break up your routes into a series of nested routes.
You add child routes using the addAll
method.
For example you can add a child router for all routes starting with /banking
var rootRouter =
router()..addAll((Router r) => r
..addAll((Router r) => r
..get('/', fetchAccountHandler)
..post('/deposit', makeDepositHandler),
path: '/account/{accountNumber}'),
path: '/banking');
Then serve up all the routes via the rootRouter
io.serve(rootRouter.handler, 'localhost', 8080)
Note in this case the full path of the deposit resource is actually
/banking/account/{accountNumber}/deposit
To try this out, fire up the server and do
curl -d 'lots of money' http://localhost:8080/banking/account/1235/deposit
You can add additional middleware to individual routes
myRouter.get('/', (_) => new Response.ok("Hello World"), middleware: logRequests());
This middleware will be applied to all requests on that route.
If you add it to a child router it will apply to all routes for that router
var bankingRouter = rootRouter.addAll((Router r) {...},
path: '/banking', middleware: logRequests()),
will apply to all banking routes and all sub routes of '/banking'
.
The Router's
addAll
method takes a typedef
that looks like
typedef RouteableFunction(Router router);
Thanks to Darts function emulator capability, this means you can easily group a set of routes together in a class.
class MyGroup {
void call(Router router) {
router..get('/', (_) => new Response.ok("Hello World"))
..get('/greeting/{name}', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
}
}
To make that a little more explicit you can extend the Routeable
class, which simply lets you call the method createRoutes
rather than call
.
And since you now have a class you may as well spin off the handlers into methods
class MyGroup extends Routeable {
void createRoutes(Router router) {
router..get('/', helloWorld)
..get('/greeting/{name}', greeting);
}
Response helloWorld(request) =>
new Response.ok("Hello World"))
Response greeting(request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
}
Then add it to another router
rootRouter.addAll(new MyGroup());
It's easy to see all the routes defined for a router using the printRoutes
function.
var router = r.router()
..get('/', (_) => new Response.ok("Hello World"))
..post('/', (_) => new Response.ok("Hello World"))
..get('/greeting/{name}{?age}', (request) {
var name = getPathParameter(request, 'name');
var age = getPathParameter(request, 'age');
return new Response.ok("Hello $name of age $age");
});
printRoutes(router);
prints
GET -> /
POST -> /
GET -> /greeting/{name}{?age}
See more detailed examples in the example
folder under the project source.
This section deals with basic customisations.
These are powerful ways to customise how the routing works and will handle most cases for customising shelf_route
.
If you need more then see the section below on Extending
The path
arguments of all the router methods accept either:
By default String value will be parsed into a UriParser which means it is expected to conform to UriTemplate.
You can also implement your own UriPattern and use that instead. For example you may prefer the :
style of path variables (e.g. :name
).
In addition it allows you to create uri path definitions and potentially share between client and server. e.g.
var accountPattern = new UriParser(new UriTemplate('/account/{accountNumber}'));
You can now use this when you define a route and on the client.
myRouter.get(accountPattern, (_) => new Reponse.ok("Hello World"));
To make it more seamless to use your own path style you can install a path adapter into the router. This will be used by all routes in this router and any child routers unless you override it somewhere.
Install the adapter by passing it to the router
function.
var colonStyleAdapter = ....; // obtain the adapter from somewhere
var myRouter = router(pathAdapter: colonStyleAdapter);
Now you can use colon style path parameters
myRouter.get('/:name', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
You can install a custom handler adapter, which allows you to transform the handlers passed into the Router
's methods. This allows for more seamless integration with other Shelf packages.
For example if you want to use ordinary Dart functions as handlers you can use a package like Shelf Bind. Shelf Bind provides such an adapter out of the box.
Install the adapter by passing it to the router
function.
import 'package:shelf_bind/shelf_bind.dart' as bind;
var myRouter = router(handlerAdapter: bind.handlerAdapter())
Now you can do
myRouter.get('/{name}', (name) => "Hello ${name}");
instead of
myRouter..get('/{name}', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
Note without installing the adapter you could still call Shelf Bind's bind
method directly.
myRouter.get('/{name}', bind((name) => "Hello ${name}"));
Note: the simplest way to include shelf_bind is to simply use shelf_rest instead of shelf_route
Similarly to how a HandlerAdapter
allows you to seamlessly integrate packages that provide alternative forms of Handlers
like Shelf Bind, a RouteableAdapter
allows you to seamlessly integrate packages that support alternative representations of a RouteableFunction
.
RouteableFunction
and RouteableAdapter
are defined as follows
typedef RouteableFunction(Router router);
typedef RouteableFunction RouteableAdapter(Function handler);
Installation
You can install the adapter when you create the top level router
var myRouteableAdapter = // create some how
var myRouter = router(routeableAdapter: myRouteableAdapter)
Now you can do
myRouter.addAll(new MyResource(), path: 'mine');
and myRouteableAdapter
will be called to adapt the instance of MyResource
into a RouteableFunction
Note: as with all the adapters you can install them at any level of the routing tree and they will take effect from that point on. For example
myRouter.addAll(new MyResource(), path: 'mine', routeableAdapter: myRouteableAdapter);
If you can't achieve the customisations you need using the above techniques then you have come to the right place. But first helps to know a little about the architecture of shelf_route
.
shelf_route
is broken into two main parts:
shelf_route
.Corresponding to the runtime routing components, are pairs of more abstract
models. These pairs are an abstract representation of a route, called a
route spec
, and an adapter
that is responsible for creating the corresponding
route component from the given route spec. More specifically there are:
SimpleRouteSpec
and SimpleRouteAdapter
which produce SimpleRoute
RouterSpec
and RouterAdapter
which produce RequestRouter
RouteSpec
and RouteAdapter
which are the root of the hierarchy
corresponding to Route
Note these models a deliberately very abstract to support the most flexibility
possible. However, in almost all circumstances you are more likely to deal
with subclasses like DefaultRouterAdapter
that provide more concrete
implementations. The support ways to provide Middleware
and adapt
route paths (e.g. supporting different path styles like ':foo') and handlers
(such as that provided by shelf_bind that allows normal Dart functions to
be used as shelf handlers)
The most advanced form of extensions to shelf_route typically work at this level but producing specs and adapters, either or both of which may be custom subclasses.
Note the Adapters inherit properties from parent routes. So often it is not necessary to provide an adapter at each node in the routing tree. A single one at the top of the tree may be enough.
SpecBasedRouterBuilder
, which is also a router spec, has methods
to add these specs to builder, such as addRoute
For now the best place to look to understand how to extend shelf_route
is the source code of shelf_rest.
See the wiki for more details on all the options
Contributions are welcome. Please:
See open issues.
attach
method from RouterNote: this is only backward incompatible if you rely on handlerAdapters not being merged. i.e. to override instead
Note: should be backward compatible with 0.8.x unless you depended on deprecated methods that have been removed
Router.addAll
to take a typedef rather than a classimplements Routeable
. To fix this simply use extends Routeable
instead.This change makes it easier to set up hierarchical routes. e.g.
var myRouter = router()
..addAll((Router r) => r
..get('/foo', (_) => new Response.ok('yeah foo'))
..get('/fum', (_) => new Response.ok('boo fum'))
..addAll((Router r) => r
..get('/yup/{title}',
(req) => new Response.ok("Hello ${getPathParameter(req, 'name')}"))
..get('/nup',
(req) => new Response.ok("Hello ${getPathParameter(req, 'name')}")),
path: '/blah/{name}'),
path: '/bar'
);
Major release with lots of changes including:
/:foo
) var myRouter = router(handlerAdapter: bind.handlerAdapter)
myRouter.get('/{name}', (name) => "Hello ${name}");
Use UriPattern and UriTemplate instead of custom path matching
{}
instead of :
getPathVariables
to encapsulate extracting path
variables stored in the requestAdded support for path variables.
Improved tests.
Add this to your package's pubspec.yaml file:
dependencies:
shelf_route: "^0.13.4"
You can install packages from the command line:
with pub:
$ pub get
Alternatively, your editor might support pub get
.
Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:shelf_route/shelf_route.dart';
Version | Uploaded | Documentation | Archive |
---|---|---|---|
0.14.3 | Oct 3, 2016 |
|
|
0.14.2 | Apr 10, 2016 |
|
|
0.14.1 | Jan 6, 2016 |
|
|
0.14.0 | Aug 30, 2015 |
|
|
0.13.5 | Aug 12, 2015 |
|
|
0.13.4 | Jul 4, 2015 |
|
|
0.13.3 | Jul 1, 2015 |
|
|
0.13.2 | Jun 26, 2015 |
|
|
0.13.1 | Jun 25, 2015 |
|
|
0.13.0 | Jun 25, 2015 |
|
|
This package version is not analyzed, because it is more than two years old. Check the latest stable version for its analysis.