Home

Castle Stronghold

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.

Quick Note

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 fieldsDescription
BeforeAction The filter is invoked before the action.
AfterActionThe filter is invoked after the action.
AfterRenderingThe filter is invoked after the rendering.
AlwaysThe 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:

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()
    {
    }
}
Google
Search WWW Search castleproject.org