23

I'm working with nopCommerce and I need to add in my only Action Filter, however, I don't want to modify the core controllers to avoid my code being overwritten when a new update is released.

I've setup my Action Filter:

public class ProductActionFilterAttribute : ActionFilterAttribute
{

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            ...
        }
        base.OnActionExecuted(filterContext);
    }

}

If I were to modify the controller, I could just add [ProductActionFilter] to the action I want it assigned to.

Is there a way I can register my custom Action Filter to a specific action without modifying the controller?

0

5 Answers 5

33

I think global filters is what you need.

Once you created the filter, register it in the global.asax:

protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();
 
    // Register global filter
    GlobalFilters.Filters.Add(new MyActionFilterAttribute());
    
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes); 
}

Add custom validation logic to filter if you don't want to apply it to all actions.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your response. A global filter does look like a suitable option and as you say, I could check for a specific controller/action. Just throwing something out there, rather than using GlobalFilters.Filters.Add, would FilterProviders.Providers.Add work the same? I'm not 100% sure how it works, but I've read it allows you to specify a certain controller/action...
@Scrooby Yes, FilterProvider may work too. Please check stackoverflow.com/questions/5312624/….
2

In NopCommerce 3.5 (the latest as of this answer, and newer than the question date), the best way I've found to add a global action filter is by creating a plugin with an IStartupTask implementation in it. This method completely avoids altering any NopCommerce core files.

The NopCommerce Application_Start event initializes the EngineContext, which creates the NopEngine instance. The NopEngine initialization finds all IStartupTask implementations, and executes them in their specified order. So an IStartupTask is the place to do anything that needs to happen on application start.

Sample code below:

public class Plugin : BasePlugin
{
    public Plugin()
    {
    }

    /// <summary>
    /// Check to see if this plugin is installed
    /// </summary>
    public static bool IsInstalled(ITypeFinder typeFinder)
    {
        IEnumerable<Type> types = typeFinder.FindClassesOfType<IPluginFinder>(true);

        if (types.Count() == 1)
        {
            IPluginFinder plugins = Activator.CreateInstance(types.First()) as IPluginFinder;
            PluginDescriptor descriptor = plugins.GetPluginDescriptorBySystemName("MyPluginName");

            if (descriptor != null && descriptor.Installed)
            {
                return true;
            }
        }

        return false;
    }
}

/// <summary>
/// Redirects to the 404 page if criteria not met
/// </summary>
public class FluffyTextureRequiredAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (Kitten.Texture != Textures.Fluffy)
        {
            var routeValues = new RouteValueDictionary();
            routeValues.Add("controller", "Common");
            routeValues.Add("action", "PageNotFound");

            filterContext.Result = new RedirectToRouteResult(routeValues);
        }
    }
}

/// <summary>
/// Does application start event stuff for the plugin, e.g. registering
/// global action filters
/// </summary>
public class StartupTask : IStartupTask
{
    private ITypeFinder _typeFinder;

    public StartupTask()
    {
        //IStartupTask objects are created via Activator.CreateInstance with a parameterless constructor call, so dependencies must be manually resolved.
        _typeFinder = EngineContext.Current.Resolve<ITypeFinder>();
    }

    public void Execute()
    {
        // only execute if plugin is installed
        if (Plugin.IsInstalled(_typeFinder))
        {
            // GlobalFilters is in System.Web.Mvc
            GlobalFilters.Filters.Add(new FluffyTextureRequiredAttribute());
        }
    }

    public int Order
    {
        get { return int.MaxValue; }
    }
}

Comments

2

If you want your filter to be registered for every action (or it is otherwise OK to do so), then MVC 3 allows you to apply Global action filters. Of course, this requires that nopCommerce is built on MVC 3, which I believe the newest version is?

Comments

1

This approach works for NopCommerce 4.10

This code will redirect "/Register" requests with "GET" Method to "YourCustomAction" action inside "YourCustomController".

Step 1: implement INopStartup

 public class NopStartup : INopStartup
 {
        public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
        {
            services.Configure<MvcOptions>(config =>
            {
                config.Filters.Add<YourCustomActionFilter>();
            });
        }

        public void Configure(IApplicationBuilder application)
        {

        }

        public int Order => 0;
    }

Step 2 :

public class YourCustomActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!(context.ActionDescriptor is ControllerActionDescriptor actionDescriptor)) return;

        if (actionDescriptor.ControllerTypeInfo == typeof(CustomerController) &&
            actionDescriptor.ActionName == "Register" &&
            context.HttpContext.Request.Method == "GET")
        {
                    string controllerName = nameof(YourCustomController).Replace("Controller", "");
                    string actionName = nameof(YourCustomController.YourCustomAction);
                    var values = new RouteValueDictionary(new
                    {
                        action = actionName,
                        controller = controllerName
                    });
                    context.Result = new RedirectToRouteResult(values);
        }
    }
}

With this approach, you can cut down the registration process and add some extra check/process then you can go on the registration process.

Comments

0

What about creating a partial class. As of version 2.60 all controllers are partials:

public partial class CatalogController : BaseNopController

You can put the filter to the class and then query the action name.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.