ASP.NET MVC: регистрировать фильтр действий без изменения контроллера

Я работаю с nopCommerce, и мне нужно добавить в свой единственный Action Filter, однако я не хочу изменять основные контроллеры, чтобы избежать перезаписывания моего кода при выпуске нового обновления.

Я установил свой фильтр действий:

public class ProductActionFilterAttribute : ActionFilterAttribute
{

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

}

Если бы я должен был изменить контроллер, я мог бы просто добавить [ProductActionFilter] к действию, которое я хочу, чтобы оно было назначено.

Есть ли способ зарегистрировать свой настраиваемый Action Filter для конкретного действия без изменения контроллера?

Ответы

Ответ 1

Я думаю, что вам нужны глобальные фильтры.

Как только вы создали фильтр, зарегистрируйте его в файле global.asax:

protected void Application_Start() {

    AreaRegistration.RegisterAllAreas();

    // Register global filter
    GlobalFilters.Filters.Add(new MyActionFilterAttribute());

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes); 
}

Добавьте специальную логику проверки для фильтрации, если вы хотите применить ее не ко всем действиям.

Ответ 2

Если вы хотите, чтобы ваш фильтр регистрировался для каждого действия (или в противном случае это ОК), MVC 3 позволяет применять Глобальные фильтры действий. Конечно, для этого требуется, чтобы nopCommerce была построена на MVC 3, и я считаю, что самая новая версия?

Ответ 3

В NopCommerce 3.5 (последний из этого ответа и более поздний, чем дата вопроса) лучший способ, которым я нашел добавить глобальный фильтр действий, - создать плагин с реализацией IStartupTask в нем. Этот метод полностью избегает изменения любых основных файлов NopCommerce.

Событие NopCommerce Application_Start инициализирует EngineContext, который создает экземпляр NopEngine. Инициализация NopEngine находит все реализации IStartupTask и выполняет их в указанном порядке. Таким образом, IStartupTask - это место, где нужно делать все, что должно произойти при запуске приложения.

Пример кода ниже:

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; }
    }
}

Ответ 4

Как насчет создания частичного класса. Начиная с версии 2.60 все контроллеры являются частичными:

public partial class CatalogController : BaseNopController

Вы можете поместить фильтр в класс и затем запросить имя действия.