Register Shopping cart (0)
You have no items in your shopping cart.
Free nopCommerce Hosting

Overriding (Intercepting) nopCommerce Controllers and Actions

Many developers have asked on nopCommerce forums for ways to override nopCommerce Controller. Others have asked about how to intercept certain events or actions (such as add to cart) on server side, run some custom codes, and continue with the original process.

The answer is simple - Action Filter. But what is Action Filter? Think of it as a custom method that gets called before or after a desired Action. If you are familiar with WebForms, you can think of an Action Filter as a Http Module

In this article, we'll talk about the power of Action Filter, and how you can use it to add behaviour to your nopCommerce code.

Background

Firstly, you would ask - is nopCommerce using Action Filter? The answer is YES! Two examples are StoreClosedAttribute.cs and PublicStoreAllowNavigationAttribute.cs.

By using these two Action Filters, nopCommerce is able to dynamically detect the current Controller and Action, as well as the current Customer Role, and conditionally close the store or restrict navigation to certain pages.

So how does this work? By default, before MVC runs an Action, it actually looks at all the registered Action Filters and execute them one by one. After all the Action Filters are executed, the Action gets called and process. And after the Action has finished processing, the Action Filters, again, get called one by one.

That means Action Filters gets called twice, once before Actions are executed (corresponding to OnActionExecuting method), and once after that (corresponding to OnActionExecuted method).

How To Write an Action Filter?

Writing Action Filters is easy. Suppose we want to add this behaviour: before a customer adds a product to cart, we want to look at the product as well as the customer's roles, and add another product (paid membership product) if the current customer is not yet a paid member. A paid member is a member with the Customer Role of Paid Member.

The first thing you want to do is create a new .CS file. You can add it anywhere, under Nop.Web or your own nopCommerce plugin if you are creating one.

Let's call this PaidMemberShipActionFilter. You need to inherit it from ActionFilterAttribute, as well as implementing IFilterProvider. So the code now looks like:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
    ActionDescriptor actionDescriptor)
{

}

What IFilterProvider do is to register the this particular class as an Action Filter so that MVC can find it. We need to tell MVC that, if the current controller is ShoppingCartController, and the current action is AddProductToCart_Details, then we execute this Action Filter; otherwise don't execute this Action Filter. The code that does so looks like:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
    ActionDescriptor actionDescriptor)
{
    if (controllerContext.Controller is ShoppingCartController &&
        actionDescriptor.ActionName.Equals("AddProductToCart_Details",
            StringComparison.InvariantCultureIgnoreCase))
    {
        return new List<Filter>() { new Filter(this, FilterScope.Action, 0) };
    }

    return new List<Filter>();
}

After the Action Filter has been registered to be discoverable by MVC, you need to write the actual code to process the membership. This is done by overriding OnActionExecuting method (it could also be OnActionExecuted in other scenario, but OnActionExecuting is what we need in our example).

I am not going to show the code of OnActionExecuting as this post is just for conceptual purposes, but your code should now read:

public IEnumerable<Filter> GetFilters(ControllerContext controllerContext,
    ActionDescriptor actionDescriptor)
{
    if (controllerContext.Controller is ShoppingCartController &&
        actionDescriptor.ActionName.Equals("AddProductToCart_Details",
            StringComparison.InvariantCultureIgnoreCase))
    {
        return new List<Filter>() { new Filter(this, FilterScope.Action, 0) };
    }

    return new List<Filter>();
}

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    // do your action here
    // first check customer role
    // if customer has Paid Member role, do nothing
    // otherwise, add product with SKU "paid-member" to the cart
}

There is one last thing we need to do. We need to explicitly register the class in AutoFac so that it'll create the instance for us when necessary. To register classes in AutoFac in nopCommerce, we'll always use DependencyRegistrar.cs:

builder.RegisterType<PaidMemberShipActionFilter>().As<IFilterProvider>();

Again, depending on the project are are creating your Action Filter, you'll use different DependencyRegistrar.cs for this purpose.

Conclusion

Action Filter is a powerful feature in MVC that allows us to do crazy things in nopCommerce. You can use Action Filter to totally bypass an Action if you want to (look at how StoreClosedAttribute.cs is using Action Filter to redirect users to another page).

And as Spiderman would say - with great power comes great responsibility. Use it responsibly! :)

Hello, welcome to pro nopCommerce!

I am Woon Cherk, an nop mvp; and this blog is the place where I share my experiences developing nopCommerce themes and nopCommerce plugins. I also give out free nopCommerce plugins and free nopCommerce themes from time to time, make sure you subscribe to our e-mail newsletter to get updates and freebies! Click here to read more about me.