web-dev-qa-db-ja.com

ASP.NET MVC:コントローラーを変更せずにアクションフィルターを登録する

NopCommerceを使用していて、唯一のアクションフィルターを追加する必要がありますが、新しい更新がリリースされたときにコードが上書きされないようにコアコントローラーを変更したくありません。

アクションフィルターを設定しました:

public class ProductActionFilterAttribute : ActionFilterAttribute
{

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

}

コントローラを変更する場合は、割り当てたいアクションに[ProductActionFilter]を追加するだけで済みます。

コントローラを変更せずに、カスタムアクションフィルタを特定のアクションに登録する方法はありますか?

20
Dan Ellis

グローバルフィルターが必要だと思います。

フィルタを作成したら、global.asaxに登録します。

protected void Application_Start() {

    AreaRegistration.RegisterAllAreas();

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

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

すべてのアクションに適用しない場合は、フィルタリングするカスタム検証ロジックを追加します。

30
sashaeve

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

すべてのアクションにフィルターを登録したい場合(または登録しても問題ない場合)、MVC 3では グローバルアクションフィルター を適用できます。もちろん、これにはnopCommerceがMVC 3上に構築されている必要があります。これは、最新バージョンだと思いますか?

2
rfmodulator

このアプローチはNopCommerce4.10で機能します

このコードは、「GET」メソッドを使用した「/ Register」リクエストを、「YourCustomController」内の「YourCustomAction」アクションにリダイレクトします。

ステップ1: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;
    }

ステップ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);
        }
    }
}

このアプローチでは、登録プロセスを削減し、チェック/プロセスを追加してから、登録プロセスに進むことができます。

0

部分クラスを作成するのはどうですか。バージョン2.60以降、すべてのコントローラーは部分的です。

public partial class CatalogController : BaseNopController

フィルタをクラスに配置してから、アクション名をクエリできます。

0