Table of contents
Filters
Filters are executed before and|or after your actions. It is useful for security, dynamic content and to keep away repetitive code.
Creating a filter
To create a filter, create a class that implements the IFilter interface, then associate the filter with your controller.
You can always create an abstract controller class and associate a filter with it and make your controllers extend it.
using Castle.MonoRail.Framework; public class AuthenticationFilter : IFilter { public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller) { if (context.Session.Contains("user")) { return true; } else { context.Response.Redirect("account", "login"); } return false; } }
The Perform return value indicates to the framework if the process should be ended. If you return false no further process will happen for the current request. It is important that you take some action before, like in the example above, issuing a redirect.
The ExecuteEnum parameter says to the filter what is the context of the invocation. It is also used on the FilterAttribute to define when you want to have the filter executed. The possible values are listed on the table below.
| ExecuteEnum fields | Description |
|---|---|
| BeforeAction | The filter is invoked before the action. |
| AfterAction | The filter is invoked after the action. |
| AfterRendering | The filter is invoked after the rendering. |
| Always | The filter is invoked around all steps. |
To associate the filter with the controller, use the FilterAttribute:
using Castle.MonoRail.Framework; [FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter))] public class AdminController : Controller { public void Index() { } }
Ordering
You can always associate more than one filter with a controller. However the order of execution cannot be guaranted. If the order of execution is important, use the ExecutionOrder property. The lower the value, the higher is the priority. For example:
using Castle.MonoRail.Framework; [FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=0)] [FilterAttribute(ExecuteEnum.BeforeAction, typeof(LocalizationFilter), ExecutionOrder=1)] public class AdminController : Controller { public void Index() { } }
For the example above, AuthenticationFilter runs before the LocalizationFilter.
Skipping filters
For some situation you may not want to execute a filter, or all filter, for one or more actions. Use the SkipFilterAttribute for those cases. For example:
using Castle.MonoRail.Framework; [FilterAttribute(ExecuteEnum.BeforeAction, typeof(AuthenticationFilter), ExecutionOrder=0)] [FilterAttribute(ExecuteEnum.BeforeAction, typeof(LocalizationFilter), ExecutionOrder=1)] public class AdminController : Controller { [SkipFilter] public void Index() { } [SkipFilter(typeof(LocalizationFilter))] public void Create() { } public void Update() { } }
For the example above we have defined that:
- No filters will be executed on the Index action
- The LocalizationFilter will not be executed on the Create action
- All filters will run on the Update action
Giving parameters to the filters
More advanced scenarios might arise where you parameterize a filter. For example, you can create a filter that is able to load text files and add each line of text to the PropertyBag. The file name is not fixed.
The first thing to do is to create a new attribute that extends FilterAttribute:
using Castle.MonoRail.Framework; [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true), Serializable] public class MyCoolFilterAttribute : FilterAttribute { private readonly string fileName; public MyCoolFilterAttribute(String fileName) : base(ExecuteEnum.BeforeAction, typeof(CoolFilterImpl)) { this.fileName = fileName; } public string FileName { get { return fileName; } } }
As you can see, the custom attribute inherits from FilterAttribute and configures the filter on the user's behalf.
Now we just need to implement the filter itselt. We also need to signalize to the framework that we are interested in gaining access to the attribute instance as we will extract information from it. This is done using the IFilterAttributeAware interface.
using Castle.MonoRail.Framework; public class CoolFilterImpl : IFilter, IFilterAttributeAware { private MyCoolFilterAttribute attribute; // Implementation of IFilterAttributeAware public FilterAttribute Filter { set { attribute = (MyCoolFilterAttribute) value; } } // Implementation of IFilter public bool Perform(ExecuteEnum exec, IRailsEngineContext context, Controller controller) { // Now you can access the parameters: String fileName = attribute.FileName; // Work // Allow the process to go on return true; } }
Now using the filter is very simple:
using Castle.MonoRail.Framework; [MyCoolFilterAttribute("customer_messages.txt")] public class CustomerController : Controller { public void Index() { } }