Detailed Guide
From Bistro
This page is a detailed guide on how bistro works, and how bistro applications should be built. For more information on the internals of bistro, go to the Runtime overview
- Disclaimer
- This guide is meant as an exhaustive overview of the full feature set of bistro. By no means is all of this information necessary or required for a given application. Happy reading!
Contents |
Controllers
The way bistro controllers are structured is one of the main differences between bistro mvc and other implementations. Throughout this guide, references will be made to the No Recruiters application, which is one of the sample applications available with bistro. No Recruiters is a community job posting and search application.
- Something to remember
- You'll see us use the term method here to describe an http request. This is because we view the http request as a REST method invocation, which results in a controller chain being generated and executed. Read on for more.
Composition of controllers
In a typical mvc framework, controllers are classes with "actions". An action is a method, invoked by the incoming http request, and there's one such action per request. While a solid solution, this tends to lead to large and complex classes. Bistro takes a different approach. Each inbound request is viewed as a REST method invocation (with appropriate http verbs optionally specified), and is serviced by a composition of multiple controllers. Each controller is a small unit of functionality, loosely coupled with the rest of the controllers. There are no static interdependencies between controllers, instead there are resource requirements, and pointcut definitions that form a controller chain in response to a request. This lets the runtime dynamically form an ordered execution chain.
Dependency Chaining
Dependency chaining is one of the ways of specifying order on a set of controllers. Resources are marked with whether they provide or consume a resource, and its name and type. The runtime then uses this information to sort the controller chain. To better illustrate this concept, let's look at the Search function in the no recruiters application. No recruiters provides the ability to search either by text, or by tag. Searching by string is a POST with a query, and searching by tag is a GET.
[Bind("postings/{contentType}")] [RenderWith("Templates/Posting/search.django")] public class Search : AbstractController { [FormField] protected string txtQuery; [Session, DependsOn] protected List<string> currentTags; public override void DoProcessRequest(IExecutionContext context) { ... } ... }
The search controller is a class that services the /postings/{contentType} request for GETs and POSTs. For POSTs it will use the value of a form field, called txtQuery. If present (and we'll talk more about the FormField and Session attributes in the state management section), it will also use the currentTags list to narrow the results to postings tagged with one of the current tags. What this controller does not do, is deal with the processing of selected and de-selected tags, nor does it know where this information comes from. This controller simply reacts to it.
The controllers that deal with this second piece, are the Tag (below) and Untag controllers
[Bind("get /postings/{contentType}/with-tag/{tagList}")] public class Tag : AbstractController { [Session, Provides] protected List<string> currentTags; protected string tagList; public override void DoProcessRequest(IExecutionContext context) { if (currentTags == null) currentTags = new List<string>(); foreach (string tag in tagList.Split(',')) if (!currentTags.Contains(tag)) currentTags.Add(tag); } }
This controller, Tag, will respond to just GET requests on /postings/{contentType}/with-tag/{tagList}. It will take the incoming tagList (which the runtime has placed into the correspondingly-named field on the class), parse it, and place the results into the currentTags field. Once again, the important thing to note here is that the Tag controller does deal with tags, but does not deal with searching, or consumption of these tags.
Now we can put these two together. When a request comes in for just /postings/something, only the search controller will be invoked. However, when a request comes in for /postings/something/with-tag/csharp, both controllers will be invoked. Tag will process the tag list and make it available on the session, and Search will consume the tag list and search by the updated tag list. There's one piece missing. In order for Search to use the most recent list of current tags, the update must occur first. That's precisely the function of the Provides and DependsOn attributes that are on the Search and Tag controllers' currentTags fields. This pair of attributes builds a resource dependency - it specifies that Tag provides a value of type List<string>, of name currentTags and that Search depends on a value of type List<string>, of name currentTags.
Pointcuts and Bind specifications
Resource dependencies specify the order in which controllers are executed. Borrowing from AOP nomenclature, we can view individual controllers as injections before or after a method execution, and specify pointcuts, as both a means of sequencing and as a primary means of specifying for what method (or resource request) a given controller should execute.
The Bind attribute specifies both of these aspects. Controllers bind to a set of methods (we will use the term method from here on to describe an http request, and italicize it when used in that context) via this attribute, but do so as an extension to the method's original intent. The intent (or Payload) of an http method is html or xml or some other structured data returned to the requester. Therefore, controllers responsible for rendering or writing output to the HttpResponse and deemed the Payload. Other controllers use the BindType parameter of the Bind attribute to specify at what point in the execution pipeline they should intercept the request, and inject themselves.
- Future direction
- In the future, the current triple of
Before/Payload/Afterwill be expanded, as currently most controllers end up falling into theBeforerange, and more granularity is needed.
This structure allows a single controller to service multiple methods, creating cross-cutting controllers that focus on a specific aspect of the application, rather than a full feature.
Referring again to the Search and Tag example from above, the Bind point of the Tag controller, is currently constrained to /postings/, however, the controller could be bound to a more generic get ?/with-tag/{tagList}, servicing all methods that have a tagging aspect, and even further decoupling the aspect of "tagging" from the function of "searching":
[Bind("get ?/with-tag/{tagList}")] public class Tag : AbstractController { [Request] protected List<string> currentTags; protected string tagList; public override void DoProcessRequest(IExecutionContext context) { ... } } [Bind("postings/{contentType}")] [RenderWith("Templates/Posting/search.django")] public class Search : AbstractController { [FormField] protected string txtQuery; [Request, DependsOn] protected List<string> currentTags; [Session] protected List<string> searchTags; public override void DoProcessRequest(IExecutionContext context) { ... } ... }
Note the slight modification from the previous version. The previous example had currentTags marked as Session and available on both. That has now been changed to Request, and a new field has been added to the Search controller - searchTags. This is because we've slightly changed the meaning of currentTags. Previously, the Search and Tag controllers were the only ones using it. As a result, it served two purposes - storage for currently selected tags, and a target for newly selected tags to be added to. Now that Tag is more generic, we don't want to necessarily say that it will always operate on tags used for searching. To that end, we make the Tag controller simply do the parsing and provided it as request-scoped output, and make the Search controller copy it from there (if present), and append it to its session variable searchTags. That way, we could add a completely unrelated controller, which supported the concept of tagging, and would not collide with Search tags:
[Bind("post user/profile")] [RenderWith("Templates/Posting/search.django")] public class UserProfile : AbstractController { [Request, DependsOn] protected List<string> currentTags; [Session] protected List<string> memberTags; public override void DoProcessRequest(IExecutionContext context) { ... } ... }
- Something to remember
- The primary function of the Bind attribute it to associate a controller class with the url(s) it will respond to. Structuring your application to be more aspect-oriented as with the tag example may not always make sense, with the exception of general aspects - configuring default values for templates, cleanup, etc. Also - remember that all binds are implicitly "begins-with" matches, so the bind "/hello/world" is actually the same thing as "/hello/world/?". See the reference for Bind for more details.
State management
State management in bistro focuses on usability and testability. One standard problem in web applications in general, and mvc applications in particular, is the need to exchange arbitrary data between controllers and views. This is typically resolved with a generic "Session" object, an untyped string key - object value dictionary, and a similar "Request" or "View" and "Form" dictionary. This poses testing and maintenance challenges, as it is difficult to enforce value requirements, and track sources of values.
Attributed Data Access
Bistro addresses these problems by extending the dependency chaining mechanism described above. A set of scoping and sourcing attributes are introduced to substitute access to the corresponding collections. As an example, to access a session field "currentTags", of type List<string>, the user needs the following
[Session] protected List<string> currentTags;
This gives the runtime enough information to access the Session, and pull down the value prior to invoking the controller, and then update the session value after the controller has been invoked. This changes session access from
private void foo() { var currentTags = (List<string>)Session["currentTags"]; foreach (string tag in currentTags) bar(tag) }
to
private void foo() { foreach (string tag in this.currentTags) bar(tag) }
The same approach is used for common operations on the Http Request - such as accessing the Cookies or Forms collections. To access field "txtQuery" from the form, simply declare field "txtQuery", and mark it as a FormField
[FormField] protected string txtQuery;
- Something to remember
- Attributes that deal with processing external data, such as FormField and CookieField will automatically change types as necessary. So if the form field contains an integer, you can declare your field to be an integer as well, and the runtime will perform the conversion for you.
- All the attributes discussed can be applied to fields of any visibility (private, protected, public, etc.)
- All the attributes can also be applied to properties, regardless of mutability. Do keep in mind though that while marking a read-only property as a
FormFieldwon't throw an exception, it won't do much good either.
- Future direction
- Currently, these attributes only take an optional name parameter, allowing the marked member to carry a different name than the resource it links to. In the future, these attributes will allow specification of formatters and validators, allowing the application to take a more active role in validation and transformation of incoming data.
Combining Attributes
One technique that this approach allows is to have the runtime pass fields from one scope or source to another by combining different attributes on the same field. The classic problem of form validation is a good example of this. Suppose there is a login form that requires the user to specify both a username and a password, and there is server-side validation to that effect. If the user fails validation, we don't want to make the user re-enter the data unnecessarily, so want to return a form that is pre-populated with the data the user previously entered. Doing so with attributes is fairly trivial:
[Bind("post /auth/logon")] [RenderWith("Views/Account/logon.django")] public class DoLogon : AuthBase { [FormField, Request] protected string username, password; ... }
with a corresponding form of
<form action="{{root}}/auth/logon" method="post"> <div> <fieldset> <legend>Account Information</legend> <p> <label for="username">Username:</label> <input name="username" value="{{username}}" /> </p> <p> <label for="password">Password:</label> <input type="password" name="password" /> </p> <p> <input type="submit" value="Log On" /> </p> </fieldset> </div> </form>
The FormField and Request attributes work in concert, the form field pulling data from the form and onto the field prior to controller execution, and the request pulling data from the field and onto the request context. That makes the data available to the template, which can then insert it into the html sent back to the browser.
- Something to remember
- Remember that while the runtime will allow any combination of attributes, certain ones may cause undesired side-effects. As an example, if a field is marked both
RequestandSession, the actual value it will carry prior to controller execution will depend on whether there was a value on the Session.
Model Management
Interaction with the model tier in bistro is done through a mapping approach; no requirements are placed on the structure of the model, or even the presence of a model. Instead, bistro provides two composable mechanisms for declaratively interacting with the model: fluent entity mappings and fluent validations.
Entity Mapping
Entity mapping is automation of the standard (and tedious) task of taking values from controller fields and placing them onto entity fields and vice versa:
// our model public class Person { public string FirstName {get; set;} public string LastName {get; set;} } // our controller [Bind("/entityTest")] public class EntityController : AbstractController { [FormField, Request] public string firstName, lastName; [Request] public Person person; public override void DoProcessRequest(IExecutionContext context) { person = new Person() { FirstName = this.firstName, LastName = this.lastName }; } }
Here, the entity controller instantiates a new Person, and populates its properties with data from the controller itself. If this controller also allowed editing an existing Person, it would then have to do the reverse operation of taking a Person instance and populating the controller's fields with values from the object.
Bistro allows you to automate this process by defining a mapping between the controller and the entity
class SimpleMapper: EntityMapper<EntityController, Person> { public SimpleMapper() { Map(c => c.firstName).To(e => e.FirstName) .Map(c => c.lastName).To(e => e.LastName); } }
This mapping class, SimpleMapper tells the bistro run time that the field firstName on the controller class should be mapped to the field FirstName on the entity class. Notice there are no requirements on the entity class - no interfaces or attributes are necessary for a type to be the target of a mapping.
Once a mapping is defined, it is attached to the source through a MapsWith attribute, and an object of type EntityMapperBase provided through the IMappable interface.
[Bind("/entityTest")] [MapsWith(typeof(SimpleMapper))] public class EntityController : AbstractController, IMappable { public EntityMapperBase Mapper { get; set;} public override void DoProcessRequest(IExecutionContext context) { person = new Person(); Mapper.Map(this, entity); } ... }
The MapsWith attribute informs the runtime of what mapper type to use for this controller, while the IMappable interface allows the runtime to provide the controller with an EntityMapperBase instance. This class provides the controller with methods for both moving data from the controller to an entity instance, and an entity instance back to the controller.
- Why not automate the map and unmap operations? Why go so far and stop short?
- One of the lessons learned from previous frameworks for us is that the actual mapping operation is not something that the runtime should control. Different factors, such as validity of data and general instantiation requirements may affect at what point and to what instance the user will want to apply a mapping operation. By providing the map and unmap operations, bistro automates the tedious tasks, but leaves control in the hands of the user.
In addition to manually listing out a mapping, the EntityMapper class defines two methods - Infer and InferStrict. Both of these methods infer a mapping by matching up field names and types between the source and target. Infer will match fields and properties for all public members defined on both types that have matching names (case-insensetive) and assignable types. InferStrict will perform a similar match, but is case-sensitive. These methods follow the Builder pattern, and return a constructed entity mapper that can be further augmented:
class SimpleMapper: EntityMapper<EntityController, Person> { public SimpleMapper() { Infer() .Except(c => c.extra) .Map(c => c.thirdField).To(e => e.baz); } }
Note how the extra field is suppressed from the automatic mapping, and then a mapping is added from thirdField to baz. This allows the user to automate mapping matching field names, and then tailor the mapping as necessary to fill remaining gaps.
Finally, for scenarios where a purely inferred mapping is sufficient, the InferMappingFor attribute allows the user to not need to write out a mapper class:
[Bind("/entityTest")] [InferMappingFor(typeof(Person))] public class EntityController : AbstractController, IMappable { public EntityMapperBase Mapper { get; set;} public override void DoProcessRequest(IExecutionContext context) { person = new Person(); Mapper.Map(this, entity); } ... }
Validation
Validation is the second aspect of the entity management model within bistro. An extensible fluent interface is defined to allow a declarative specification of validation rules on controllers. This model also includes a rendering framework-specific client-side plugin for generating complementary client-side validation rules based on the server side definition, covering both ends of the spectrum.
Server
Validation rules are specified in a similar fashion to mapping rules. A specialized per-validation-target class is defined, and associated to its target via an attribute:
public class SimpleValidator : Validator<EntityController> { public SimpleValidator() { Define( Value(c => c.firstName).IsRequired("First Name is required")) .And( Value(c => c.lastName).IsRequired("Last Name is required")); } } // our controller [Bind("/entityTest")] [ValidateWith(typeof(SimpleValidator))] public class EntityController : AbstractController, IValidatable { public List<IValidationResult> Messages { get; set; } public bool IsValid { get; set; } ... }
The validation model does require that target classes, while are not necessarily controllers, implement the IValidatable interface. This interface defines the Messages and IsValid properties which are needed for the runtime to inform the target of its state. For controllers, validation is performed after the Initialize method has been called, but before DoProcessRequest. Validation for non-controller types is left to the user. The validator shown above can be easily instantiated and applied to the target class without bistro intervention.
Since validation can be applied to non-controller types, a preferred scenario is that the validation rules for a given entity be associated to that entity, and not replicated for every controller that needs to interact with it. To that end, for controllers mapped to entities, a ByMapping method is made available:
This code sample modifies the Person entity to implement theIValidatableinterface, allowing it to become the target of a validator.public class SimpleValidator : Validator<Person> { public SimpleValidator() { Define( Value(c => c.firstName).IsRequired("First Name is required")) .And( Value(c => c.lastName).IsRequired("Last Name is required")); } } [ValidateWith(typeof(SimpleEntityValidator))] public class Person: IValidatable { ... public List<IValidationResult> Messages { get; set; } public bool IsValid { get; set; } }Once this is done, we can define the validation on the controller to be that of the mapped entity with the ByMapping method. This method will take all the fields that have mappings from the controller to the entity, and move their validation rules from the corresponding entity fields. And just like in the entity mapping model, the
ByMappingmethod follows the builder pattern, allowing the user to augment the validation rules with additional local rules.class EntityControllerValidator: Validator<EntityController> { protected override void Define() { ByMapping(); } } [Bind("/entityTest")] [ValidateWith(typeof(EntityControllerValidator))] [InferMappingFor(typeof(Person))] public class EntityController : AbstractController, IMappable, IValidatable ...One further step, following again the entity mapping convention, an InferValidationFrom shortcut attribute is defined to remove the need for creating an empty class when only the rules of the target entity are enough
[Bind("/entityTest")] [InferValidationFrom(typeof(Person))] [InferMappingFor(typeof(Person))] public class EntityController : AbstractController, IMappable, IValidatable ...
Client
- Note
- The following section describes the client-side validation extensions made available to the ndjango rendering framework. These extensions are made as part of the
Bistro.Extensionsproject, and are the only ones currently available. Extensions for other rendering and validation frameworks can be easily developed, outside of the bistro runtime.
The validation rules defined through the mechanisms defined above are fully discoverable, and the rules are associated into global "namespaces". The Validator class defines two aliasing methods As and For, the former for grouping validation rules into global namespaces, the latter for allowing form elements to carry a different name than server-side fields and properties. This information, along with the structure of the validation rules and their parameters are made available through the ValidationRepository class.
The ndjango {% validate %} tag in the BistroIntegration project leverages this discoverability to transform validation rules defined server-side into calls to the jQuery validation plugin that mimics the server-side rules with client-side counterparts.
Applying the As aliasing method to the aforementioned SimpleValidator, we have:
public class SimpleValidator : Validator<EntityController> { public SimpleValidator() { Define(As("Contacts") .Value(c => c.firstName).IsRequired("First Name is required")) .And( Value(c => c.lastName).IsRequired("Last Name is required")); } }
The namespace "Contacts" was chosen to match the form id in the view for consumption by the jBistroValidation.js jQuery plugin. The jBistroValidation plugin expects the form id to match the namespace being validated. Of course, any client-side validation framework and/or namespace could be used.
The following <script> block is then emitted by Bistro wherever the ndjango {% validate "Contacts" %} tag is present.
if (validation == undefined) var validation = new Array(); validation["Contacts"] = { ns: "Contacts", rules: [ { field: "firstName", attributes: [ { name: "Bistro.Extensions.Validation.Common.RequiredValidator", message: "First Name is required" } ] }, { field: "lastName", attributes: [ { name: "Bistro.Extensions.Validation.Common.RequiredValidator", message: "Last Name is required" } ] } ] };
The jBistroValidation plugin will then consume this script block and leverage the power of the jQuery validation plugin by generating equivalent rules and delegating further processing. The jBistroValidation plugin simply interprets the Bistro validation rules. The plugin can be extended to process custom Bistro validation rules.
Instantiating the plugin only requires the following jQuery call.
$('form').bistroValidation();
In this example, all forms are collected and wired for validation. Of course the scope of forms wired could be changed by using a different jQuery selector.
The following is a sample HTML page containing a form which takes advantage of client/server-side validation. Note the three JavaScript references in the <head> tag.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title> {% block Title %}Home Page{% endblock %} </title> <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery.validate/1.5.5/jquery.validate.js"></script> <script type="text/javascript" src="{% url 'Scripts/jBistroValidation.js' %}"></script> <script type="text/javascript"> $(function() { $('form').bistroValidation(); }); </script> </head> <body> <form id="Contacts" method="get" action=""> {% validate "Contacts" %} <p> <label for="first-name-textbox">First Name</label> <input id="first-name-textbox" name="firstName" /> </p> <p> <label for="last-name-textbox">Last Name</label> <input id="last-name-textbox" name="lastName" /> </p> <p> <input type="submit" value="Submit" /> </p> </form> </body> </html>
The jquery.validate plugin will wire the Bistro client-side rules, as interpreted by jBistroValidation, to the appropriate form fields and intercept the submit button behavior. If the validation rules are satisfied, the plugin will release its control and allow the browser or other controlling entity to resume control. If validation fails, jquery.validate will intercede and perform some action. These actions are highly customizable. See jquery.validate for details.
Security
Bistro follows a delegated model of Authentication, Authorization and Access, enforcing role-based access to methods, and delegating authentication and authorization to the user's application.
Access Control
Access policy in bistro is defined through a set of cross-cutting bind rules specified on security controllers. The binds follow standard binding rules, and specify that all methods that match the bind rule are subject to the policy defined on the given security controller.
The runtime invokes the HasAccess method on each ISecurityController, delegating the access work to it. In the default implementation (SecurityController), the policy itself is specified through a combination of allow and deny attributes. Deny rules are applied first, followed by Allow rules, letting an allow override a deny. A single controller may carry multiple deny and allow attributes, and multiple security controllers may match a given method, forming a comprehensive security policy.
As an example, the following set of controllers specify that all resources within the application require authentication through the /auth/logon method, that the administration module requires the 'admin' role, and that the login method, despite matching the deny-all rule, should still be accessible to anonymous users
[Bind("?")] [Deny("?", OnFailure = FailAction.Redirect, Target = "/auth/logon")] public class GlobalSecurity : SecurityController { } [Bind("/auth/logon")] [Allow("?")] public class LogonSecurity : SecurityController { } [Bind("/admin/")] [Deny("*", OnFailure = FailAction.Redirect, Target = "/auth/logon")] [Allow("admin")] public class AdminSecurity : SecurityController { }
- Why can't the allow and deny attributes be specified directly on controllers? Why this separate SecurityController business?
- The thought is that a single controller class is just a small piece of the overall request. Marking one controller, but not another, both of which may be invoked by a given method, creates unpredictable scenarios, and a potential security hole. This mechanism forces the separation of security policy definition from functionality definition, and makes the user define the security policy over the methods the application exposes, and not the controllers that it houses.
- How do I go back after authentication?
- If your user was sent to a "fail action" upon failing a security requirement, and once they've authenticated successfully, you will probably want to redirect them to wherever it is that they were trying to go originally. When a fail scenario occurs, the runtime captures the originally requested url in a query string parameter called
originalRequest. While it is your responsibility to hang on to that value past the initial redirect, you can use that to eventually send the user on their way.
- Something to remember
- Security controllers can have functionality just like any other controller. The only difference is that security controllers are run prior to all other controllers, to make sure that all security checks pass before any user code runs. This also means that while you can build resource dependencies between security and user controllers, having a security controller
DependOnorRequirea user controller will lead to a runtime exception.
- Future direction
- Currently, every
Denyattribute must specify aFailAction, or the system will throw a HTTP:403 WebException. This does mean that logon page needs to be specified at everyDenyattribute. A global location for this information will be provided in future Bistro releases.
Authentication
Authentication in Bistro is delegated to the application. When a request fails security policy, the FailAction of the attribute that failed is invoked. Typically, the fail action is a redirect to a logon page. From the Bistro standpoint, a logon page is just another bind point, and requires no special nomenclature. It is the user application's responsibility to gather credentials and authenticate the user in whatever manner is appropriate for the application. Once authenticated, the application needs to call the IExecutionContext.Authenticate, supplying the authenticated user as the IPrincipal parameter.
To sign the user out, simply call the Authenticate method, supplying null as the parameter.
Extensibility
One of the aspects of bistro is extensibility. The entire bistro runtime is composed of a set of interfaces defining the major components. The key components of use for more common extensibility concerns are listed and described below
Controller Manager
The controller manager is the keeper of the entire set of controllers available in the runtime. The runtime interacts with the manager to retrieve instances of controllers for execution, and return controllers back to the manager once execution is complete.
Typical needs for changing this component stem from a different source of controllers. The default implementation scans all loaded assemblies for classes implementing the IController interface. As an example, the [[|FSharp|FSharpExtensions]] library uses its own controller manager to also scan for functions that are marked with the Bind and ReflectedDefinition attributes.
Controller Handler
The controller handler is responsible for marshalling a single type of controller. There is typically one instance of controller handler per controller type. The handler is responsible for instantiating, writing to and reading from controller instances. It is the handler that also makes the decision whether a given type of controller needs to be instantiated per-request, or of it can be pooled or used concurrently.
Typical needs for changing this component are changing how controllers are insantiated and populated. The FSharpExtensions library uses a custom handler to return an anonymous class as a wrapper for the controller function that it marshals.
Another example would be to continuously reuse rendering controllers, without re-instantiating them (which is the default behavior):
To do so, we would define a new ControllerHandlerFactory to inject the instantiation of a different type of controller (this method is called once per controller type, so reflection overhead is minimal):
if (((Type)descriptor.ControllerType).IsAssignableFrom(typeof(RenderingController))) return new RenderingControllerHandler(descriptor, application.LoggerFactory.GetLogger(typeof(ControllerHandler))); return new ControllerHandler(descriptor, application.LoggerFactory.GetLogger(typeof(ControllerHandler)));
Then, the new handler, rather than instantiating a new RenderingController for each request, could just follow a singleton pattern:
public class RenderingControllerHandler : Bistro.Controllers.IControllerHandler { /// <summary> /// The controller managed by this handler. The specific bind-point is determined at the point of invocation. /// </summary> ControllerDescriptor descriptor; /// <summary> /// The instance to be used by the incoming requests /// </summary> RenderingController instance; private ILogger logger; protected internal RenderingControllerHandler(ControllerDescriptor descriptor, ILogger logger) { this.descriptor = descriptor; this.logger = logger; this.instance = Activator.CreateInstance(descriptor.ControllerType as Type); instance.Initialize() } public virtual IController GetControllerInstance(ControllerInvocationInfo info, HttpContext context, IContext requestContext) { return instance; } public virtual void ReturnController(IController controller, HttpContext context, IContext requestContext) { } }
Controller Dispatcher
The controller dispatcher is responsible for computing the controller execution chain based on an incoming http request.
Common reason for extension is performance considerations. The default dispatcher implementation is geared towards effectively servicing all requests. However, given a weighted distribution of requests, improvements for a majority can be made at the cost of slowing down other requsts. Specifically - if a majority of the application is known to not contain parameters in urls or wild-cards, computation of the execution chain can be sped up significantly.
Security
As with other components, the security structure in bistro can be modified to incorporate other types of validations. The default implementation relies on the implementation of IPrincipal.IsInRole(string) provided by the application. However, that is just a single implementation. Others are possible. The basic pattern for security in bistro is to define a single worker class that knows how to determine if a user satisfies the requirements of the security policy, and then use empty subclasses of the worker class to actually specify policy. Specifically, the example of
[Bind("?")] [Deny("?")] public class DefaultSecurity: SecurityController {}
creates a controller in the system, bound to all incoming requests, that is a subclass of SecurityController, marked with the Deny("?") attribute. It is then the inherited SecurityController logic of HasAccess that reads and interprets what the Deny("?") specification means. By the same token, we can supply our own definition.
Suppose we wanted to make sure that a given method was only accessed from certain ip addresses. We could supply the list of valid addresses through some configuration, and then provide our own ISecurityController implementation to that effect:
public class IPSecurityController : IController, ISecurityController { #region IController Members private string requestIP; private List<string> blockedIPs; public void ProcessRequest(HttpContext context, IContext requestContext) { // our Stateful property informs the runtime that this controller // cannot be accessed concurrently. We're safe to set member fields. requestIP = context.Request.UserHostAddress; } public bool Reusable { get { return true; } } public bool Stateful { get { return true; } } public void Initialize() { // load our list of IP addresses here } public void Recycle() { requestIP = null; } public MemberInfo GlobalHandle { get { return GetType(); } } #endregion #region ISecurityController Members public bool HasAccess(IContext context, IDictionary<string, KeyValuePair<FailAction, string>> failedPermissions) { // again, this controller is thread-safe, so we can check the value directly return blockedIPs.Contains(requestIP); } #endregion }
Now, whenever we want to apply this policy to a method, we can do so the same way we do with standard security:
[Bind("/ip-restricted/content")] public class ContentSecurity: IPSecurityController { }
Extensibility with other frameworks
Dependency Injection with StructureMap
Before getting deep into StructureMap specifics, let's first understand the "Dependency Injection" pattern. So why would one even care about such a pattern? Well, like all other patterns, it was created to solve a problem where we may have classes that can potentially be interchangeable but those classes should not be hardcoded within your code. Let's look at the simplest example. Suppose we have a person who likes to drink soft drinks:
public class Person { public Person(IDrink drink) { Drink = drink; } public string DrinkName() { return Drink.Name; } public Int32 DrinkCalories() { return Drink.Calories; } public IDrink Drink { get; set; } }
The IDrink interface looks like this:
public interface IDrink { string Name { get;} Int32 Calories { get; } }
Seems simple enough. We take in some implementation of a IDrink and we have some person who's drinking a specific soft drink. So let's create one:
public class Sprite : IDrink { public string Name { get { return "Sprite"; } } public Int32 Calories { get { return 140; } } }
So most folks are used to doing something like this:
static void Main(string[] args) { Person person = new Person(new Sprite()); Console.WriteLine(person.DrinkName()); Console.WriteLine(person.DrinkCalories()); }
It seems innocent enough. We have a clean interface and our Main method simply passes in the concrete instance of a soft drink. Well, not so fast. Suppose, you are given a requirement that says "Every person in our company should no longer be drinking Sprite. Our research indicates that drinking MineralWater will make all people at our company more productive.". Fine you say, let's simply update our Main method and switch an implementation from Sprite to MineralWater.
public class MineralWater : IDrink { public string Name { get { return "Mineral Water"; } } public Int32 Calories { get { return 0; } } }
So now we have a concrete class that we can easily switch. However, don't you need to recompile your class? True. Oh, but we can use reflection and simply load those classes and configure them via some configuration setting.
Introduction to StructureMap
Instead of fiddling with Assembly.Load, how about using a framework with an intuitive api? How about using a framework that can help you do ctor and setter injection? Give you an ability to inject using attributes, a configuration file or an intuitive DSL-type language[1].
Dependency Injection
By using StructureMap's DSL, we can have an outside framework that knows about the application as a whole and have it inject the actual concrete class for us. So let's write some intitialization logic:
ObjectFactory.Initialize(c => { c.UseDefaultStructureMapConfigFile = false; c.ForRequestedType<IDrink>() .TheDefault.Is.OfConcreteType<MineralWater>() });
So what does the above mean? We simply said that anywhere in your code where you have a class that takes an interface IDrink, please inject a concrete class MineralWater. So our Main mdethod can actually look like this now:
static void Main(string[] args) { Person person = ObjectFactory.GetInstance<Person>(); Console.WriteLine(person.DrinkName()); Console.WriteLine(person.DrinkCalories()); }
Look that we did not have to specify the actual drink. Why? Because it actually got injected for us by StructureMap. Hence, the reason it's called "Dependency Injection".
Integration with Bistro
Bistro by itself scans the bin directory for any assembly that contains ISecurityController or IController so it itself injects controllers from outside automatically. What Dependency Injection frameworks like StructureMap help you with is injecting your business logic within your controllers.
So how do we initialize StructureMap within Bistro? Bistro provides a Bistro.Application class that allows you to insert any logic that you need upon initialization. That is exactly what we need. We need to configure StructureMap within this class so the initialization logic will be hit only once. So let's create one:
public class CustomApplication : Bistro.Application { public CustomApplication (ILoggerFactory loggerFactory) : base(loggerFactory) { this.InitializeDependencyInjectionContainer(); } private void InitializeDependencyInjectionContainer() { ObjectFactory.Initialize(c => { c.UseDefaultStructureMapConfigFile = false; c.ForRequestedType<IDrink>() .TheDefault.Is.OfConcreteType<MineralWater>() }); } }
Wait a minute. Don't we still have to recompile the code if we need to switch the concrete class? No worries, StructureMap to the rescue. Let's change our CustomApplication logic a little bit. We need to tell StructureMap to simply scan all assemblies that have a specific type as in:
public class CustomApplication : Bistro.Application { public CustomApplication (ILoggerFactory loggerFactory) : base(loggerFactory) { this.InitializeDependencyInjectionContainer(); } private void InitializeDependencyInjectionContainer() { ObjectFactory.Initialize(c => { c.UseDefaultStructureMapConfigFile = false; c.Scan(x => { x.Assembly("CustomApplication.Plugins"); x.AssemblyContainingType<IDrink>(); }); }); } }
It still seems unfinished. We told StructureMap to load an assembly called "CustomApplication.Plugins" that contains an interface IDrink. It would be cleaner if I could just tell it to scan my Plugins directory and search for any assembly that contains the word "Plugin" as in:
public class CustomApplication : Bistro.Application { public CustomApplication (ILoggerFactory loggerFactory) : base(loggerFactory) { this.InitializeDependencyInjectionContainer(); } private void InitializeDependencyInjectionContainer() { ObjectFactory.Initialize(c => { c.UseDefaultStructureMapConfigFile = false; c.Scan(x => { x.AssembliesFromPath("Plugins", a => a.GetName().Name.Contains("Plugin")); x.AssemblyContainingType<IDrink>(); x.LookForRegistries(); }); }); } }
That seems better now. On Bistro startup, we scan the "Plugins" directory and any assembly with "Plugin" in its name that contains IDrink interface will be injected. The actual logic that contains code describing which concrete class to inject can now be moved to a Registry class within some plugin assembly. So you define the classes to be injected in the registry class hosted in an assembly that you can drop into the Plugin directory, leaving your main application untouched. Registries allow the developer to organize the injection groupings and means you can have as many as you like. That really helps when you have many classes that require injection and you do not want to clobber your code that can make it unreadable. Having a simple registry class like below within your plugin makes for a cleaner and more maintainable code and the best part is that the plugin itself tells the main application what to inject.
public class DrinkRegistry : Registry { public DrinkRegistry () { ForRequestedType<IDrink>() .TheDefault.Is.OfConcreteType<MineralWater>() } }
Additionally, you may have a need to differentiate in code what you want to inject. May be in the Main method, you want a specific injection to happen but in some other class, you want a different concrete implementation. That is all possible with Profiles or Instance names.
This is only a fraction of StructureMap's power. Patterns like Chain of Responsibility, Decorator and Composite can all be refactored using StructureMap for a more loose-coupled design. You can even use a Service Locator[2] pattern to rid the actual dependency on StructureMap itself.

