Wiki
Clone wikishelf_bind / Binding Inference
Shelf Bind infers the bindings between the request path parameters and the parameters of your functions.
This page explains in detail how the inferencing works and how you can override it.
Sample Code
The following code is used as examples in the explanations
Person Class
class Person { final String name; Person.build({this.name}); }
class Account { String accountId; toJson() => { 'accountId': accountId }; }
Our Handler Function
Future<Account> createAccount(String accountId, Person person)
A Shelf Handler for our Handler Function
var handler = bind(createAccount);
var router = route.router() ..put('/account/{accountId}{?name}', handler);
accountId
path segmentname
query parameter
You can then serve this up as follows
io.serve(router.handler, 'localhost', 8080);
Basic Algorithm
The default behaviour is as follows.
For each parameter in the handler function:
- if the parameter is of type Request then the shelf Request will be passed unchanged
- if the parameter is a simple type then it will be passed the value of a Shelf Route path parameter of the same name.
- the value of the path parameter will first be converted to the type of the function parameter
- otherwise it will be treated as a complex type binding which will either:
- bind request parameters to named parameters (with the same name) of a constructor called build if present OR
- bind to property setters of the same name
In the example above the route (/account/{accountId}{?name}) has two parameters:
- accountId
- name
The createAccount function has two parameters:
accountId
of typeString
person
of typePerson
From this Shelf Bind inferred the following:
- bind createAccount's accountId parameter to the path parameter accountId
- bind createAccount's person parameter to a new instance of Person and bind the path parameter name to the constructor parameter of Person.build called name
Overriding Default Bindings
Shelf Bind allows you to override all the default (inferred) bindings to give you full control over the process.
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
The constructor looks like
Person.build({String firstname}) : this(firstname);
The handlers for Shelf Bind differ from the standard Shelf handlers as folows:
- They take the bound object as the first argument
- They optionally take the request as a second argument
- They can return either a Response / Future<Response> as normal or else any object that has a toJson method. For example Person / Future<Person>
The handler in this example looks like
Person _handlePerson(Person person) { return person; }
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
Query Params
To bind to a query parameter
final routeHandler = route.router() ..get('/person{?name}', bind.bind(Person, {'name': #firstname}, _handlePerson)) .handler
'/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.
Details: Handler Functions
Handler functions in Shelf Bind can take the following forms:
1/ A handler with an additional argument
Response handler(someType boundObject, Request request); OR Future<Response> handler(someType boundObject, Request request);
2/ A handler without the Request argument
Response handler(someType boundObject); OR Future<Response> handler(someType boundObject);
3/ A handler that returns some other type (that has a toJson method)
Note the return type can be any type, not necessarily the same as the boundObject
someType handler(someType boundObject, Request request); OR Future<someType> handler(someType boundObject, Request request); OR someType handler(someType boundObject); OR Future<someType> handler(someType boundObject);
Updated