Reputation: 5637
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?
Upvotes: 23
Views: 27339
Reputation: 3738
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?
Upvotes: 2
Reputation: 346
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.
Upvotes: 1
Reputation: 9617
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 want to apply it not to all actions.
Upvotes: 33
Reputation: 3012
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; }
}
}
Upvotes: 2
Reputation: 6528
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.
Upvotes: 0