shelf_bind 0.2.1 shelf_bind: ^0.2.1 copied to clipboard
A binding handler for shelf
Binding Handler for Dart Shelf #
Introduction #
Provides Shelf middleware for binding Shelf request and response data to class properties.
Shelf Bind currently supports the following bindings:
- Binding to Shelf Route path variables
- Binding request Json Body
No response bindings yet.
Using #
Binding to Shelf Route Path Variables #
Binding via Constructor
Binding a simple path variable
final routeHandler = route.router()
..get('/person/{name}', bind.bind(Person, {'name': #firstname}, _handlePerson))
.handler
This binds the path variable called name as defined in the path /person/{name} to the named argument called firstname of the Person.build constructor and calling the _handlePerson function when matched.
The constructor looks like
Person.build({String firstname}) : this(firstname);
The handlers for Shelf Bind differ from the standard Shelf handlers by an extra argument which is the bound object.
_handlePerson(Person person, shelf.Request request)
Note build is the default name for the constructor. It can be overriden by passing the constructor parameter to bind
final routeHandler = route.router()
..get('/person/{name}', bind.bind(Person, {'name': #firstname}, _handlePerson,
constructor: #foo))
.handler
Would look for a constructor Person.foo({String firstname})
.
Query Params
To bind to a query parameter
final routeHandler = route.router()
..get('/person{?name}', bind.bind(Person, {'name': #firstname}, _handlePerson))
.handler
The only change was that path passed to get is now '/person{?name}'
Binding via Properties
If you prefer, you can bind via properties (fields or setters) instead of via the constructor.
To do that you specify the bindMode named argument as BindMode.PROPERTY (default is BindMode.CONSTRUCTOR) like
final routeHandler = route.router()
..get('/person/{name}', bind.bind(Person, {'name': #firstname}, _handlePerson,
bindMode: BindMode.PROPERTY))
.handler
plus you must have a default (unnamed) constructor that takes no mandatory arguments. For example a constructor like
class Person {
String name;
Person();
}
Binding to a Json Body #
Binding to a Json body is very similar. Instead you call the bindJsonBody function.
final routeHandler = route.router()
..post('/person', bind.bindJsonBody(Person, _handlePerson)))
.handler
As this is binding to a Json map it does not take the binding map as above.
Example #
Full source at example/binding_example.dart
The domain objects
class Person {
final String name;
Person(this.name);
Person.build({String name}) : this(name);
Person.fromJson(Map json) : this(json['name']);
Map toJson() => { 'name': name };
String toString() => 'Person[name: $name]';
}
A simple handler that just echos back the bound person
shelf.Response _handlePerson(Person person, shelf.Request request) {
print(person);
return new shelf.Response.ok('${request.method} ${request.requestedUri} '
'=> ${person.toJson()}\n');
}
The routes and bindings
final pathBindHandler = bind.bind(Person, {'name': #name}, _handlePerson);
var router = (route.router()
..get('/person/{name}', pathBindHandler)
..get('/person{?name}', pathBindHandler)
..post('/person', bind.bindJsonBody(Person, _handlePerson)))
.handler;
var handler = const shelf.Stack()
.addMiddleware(shelf.logRequests())
.addHandler(router);
Serve it up
io.serve(handler, 'localhost', 8080).then((server) {
print('Serving at http://${server.address.host}:${server.port}');
});
}
Try it out
First route
curl http://localhost:8080/person/fred
The output should look like
GET http://localhost:8080/person/fred => {name: fred}
Second route
curl http://localhost:8080/person?name=fred
Third route (json POST)
curl -d '{"name": "fred"}' http://localhost:8080/person
TODO #
See open issues.
Contributing #
If you want to contribute, please:
- fork the repo and implement your changes with good unit test coverage of your changes
- create a pull request
- create an issue detailing the change and link the PR to it