shelf_route 0.11.0+1 shelf_route: ^0.11.0+1 copied to clipboard
Routing middleware for Shelf
Router for Dart Shelf #
Introduction #
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:
- focuses solely on routing, leaving other concerns like binding, validating, error handling etc to other middleware components
- uses Shelf Path for storing path parameters in the Shelf Request context for interoperability with compatible middleware like Shelf Bind
- supports defining routes heirarchically for greater modularity
- supports custom path formats
- supports custom handler adapters
- supports custom adapters for anything capable of creating routes
This makes it very versatile and lets you mix and match it with your other favourite Shelf middleware components.
Using #
Basics #
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);
}
Http Methods #
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"));
Path Parameters #
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/fredquery 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");
}
Hierarchical Routers #
To improve modularity you can break up your routes into a series of nested routes.
There are two options for setting up hierarchical routes.
Using Router.child()
For example you can add a child router for all routes starting with /banking
var rootRouter = router();
var bankingRouter = rootRouter.child('/banking');
Now you can add routes to the banking router as normal
bankingRouter
..get('/account/{accountNumber}', fetchAccountHandler)
..post('/account/{accountNumber}/deposit', makeDepositHandler);
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
Using Router.addAll()
Using the addAll
method is more compact and is particularly ideal for smaller applications. The previous example can be set up as follows
var rootRouter =
router()..addAll((Router r) => r
..addAll((Router r) => r
..get('/', fetchAccountHandler)
..post('/deposit', makeDepositHandler),
path: '/account/{accountNumber}'),
path: '/banking');
Route Specific Middleware #
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.child('/banking', middleware: logRequests());
will apply to all banking routes.
Custom Path Formats #
The path
arguments of all the router methods accept either:
- a String or
- a UriPattern
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"));
Installing a Custom Path Adapter
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')}"));
Custom Handler Adapters #
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}"));
Custom Route Creators #
If you have some method of creating routes then it's easy to glue in.
First implement the Routeable
interface
class MyRouteCreator extends Routeable {
void createRoutes(Router router) {
router..get('/', (_) => new Response.ok("Hello World"))
..get('/greeting/{name}', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}"));
}
}
Then add it to another router
rootRouter.addAll(new MyRouteCreator());
This can also be done with a function
rootRouter.addAll((Router router) {
router..get('/', (_) => new Response.ok("Hello World"))
..get('/greeting/{name}', (request) =>
new Response.ok("Hello ${getPathParameter(request, 'name')}")));
Custom Routeable Adapters
Similarly to how a HandlerAdapter allows you to seamlessly integrate packages like Shelf Bind, a RouteableAdapter allows you to seamlessly integrate packages like Shelf Rest.
Installation
import 'package:shelf_rest/shelf_rest.dart' as rest;
var myRouter = router(routeableAdapter: rest.routeableAdapter)
Now you can do
myRouter.addAll(new MyRestResource(), path: '/rest');
Note without installing the adapter you could still call Shelf Rests bindResource
method directly.
myRouter.addAll(rest.bindResource(new MyRestResource()), path: '/rest');
Printing Routes #
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}
Examples #
See more detailed examples in the example
folder under the project source.
More Information #
See the wiki for more details on all the options
Contributing #
Contributions are welcome. Please:
- fork the repo and implement your changes with good unit test coverage of your changes
- create a pull request and include enough detail in the description
TODO #
See open issues.