How can I intercept all controller calls in an MVC application? - asp.net-mvc-3

Is there a quick method for intercepting all controller calls in MVC-3?
For logging and testing purposes, I'd like to build a tool that can intercept all controller calls, and log which controller was called, with which message, at what time.

I can't remember where I got this from, but I was looking around for something similar a while back and found an article or something somewhere that contained this logging filter:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = string.Format("{0} controller: {1} action: {2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
To use it, just add it to the global filters in global.asax:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new LogActionFilter());
}
I'll have a look now to see if I can find the source.
Edit: Found it. It was from this question.

Depending on how big the site is already, you could create a class in the hierarchy between the framework's Controller class and your main controllers.
Something like
public class MyBaseController : Controller {
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
// your logging stuff here
base.OnActionExecuting(filtercontext);
}
}
Then the rest of your controllers can inherit from this, e.g.
public class HomeController : MyBaseController {
// action methods...
}

You can use your own controller factory and register it as well:
From: (many example on the net - insert logging where you want)
adapted from: http://www.keyvan.ms/custom-controller-factory-in-asp-net-mvc
using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
namespace IControllerFactorySample.ControllerFactories
{
public class YourControllerFactory : IControllerFactory
{
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
IController controller = Activator.CreateInstance(Type.GetType(controllerName)) as IController;
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
}
dont forget to register it in global.asax.cs
ControllerBuilder.Current.SetControllerFactory(
typeof(YourControllerFactory));

there is a routing debugger developed by Phil Haack
ASP.Net Routing Debugger

Related

How to develop action filter controller or action method specific

i was trying to develop a custom action filter which will check cookie is enable or not. if cookie is not enable then redirect use to a specific error page.here is my code.
public class CheckCookieAttribute : FilterAttribute, IActionFilter
{
public string prmAction{get;set;}
public string prmController{get;set;}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Request.Cookie["YourCookie"]==null)
{
filterContext.Result = controller.RedirectToAction(prmAction,prmController)
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
//The action filter logic - after
}
}
now i am using like
[CheckCookie(prmAction="MyAction",prmController="MyController")]
due to lack of good knowledge i am not being able to develop attribute driven check for cookie enable or disable.
i want to develop a code in such a way as a result i should not pass any controller name or action name. i like to use code like
[HttpPost]
[CheckCookieAttribute]
public ActionResult Save(Person oPerson)
{
return View();
}
[CheckCookieAttribute]
public class HomeController : Controller
{
public ActionResult Index()
{return View();}
public ActionResult About()
{return View();}
}
}
where i will not provide any name of controller or action name. just guide me what i need to change in my code. thanks
It seems that what you are trying to accomplish is already built into ASP.NET MVC.
I would use the [Authorize] attribute (http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.108).aspx) where you want to check if the user has a cookie.
If you want to redirect the user to a specific controller/action when the user is not authorized, you can use the following attribute instead:
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Error",
action = "Unauthorized"
})
);
}
}
See ASP.NET MVC 4 Custom Authorize Attribute with Permission Codes (without roles)
Then you would use it by using:
[HttpPost]
[AuthorizeUser]
public ActionResult Save(Person oPerson)
{
return View();
}
Or if you want exactly what you asked for you can do it this way:
public class CheckCookieAttribute : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.Cookies["YourCookie"] == null)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { {"controller", "MyController"}, {"action", "MyAction"}});
}
else
{
base.OnActionExecuting(filterContext);
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
//The action filter logic - after
}
}

Defining authentication requirements in MVC3 by action method attribute

I've got an MVC3 application with 4 levels of authentication, and 4 base controllers that tie to each one:
Unauthenticated - BaseController
User - BaseAuthController : BaseController
Advisor - BaseAdvisorController : BaseAuthController
Admin - BaseAdminController : BaseAuthController
Right now I have a series of overrides in place for special cases... e.g. a controller that is typically only for admins can have an action method or two that advisors can use... I have the overrides defined as strings in an array.
public class BaseAuthController : BaseController
{
/// <summary>
/// Enter action names in here to have them ignored during login detection
/// </summary>
public string[] NoAuthActions = new string[] { };
/// <summary>
/// Actions only usable by Users+
/// </summary>
public string[] UserOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Advisors+
/// </summary>
public string[] AdvisorOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Admins+
/// </summary>
public string[] AdminOnlyActions = new string[] { };
.......
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//special code here to determine what to do with requested action...
//verifies that user is logged in and meets requirements for method...
//if not, redirects out to another page...
}
}
At the controller level I have them defined like this...
public class GrowerController : BaseAdminController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
UserOnlyActions = new string[] { "GrowthStageSelection" };
AdvisorOnlyActions = new string[] { "Landing", "SeedSelection", "UpdateProjection",
"NitrogenApplications", "DeleteNitrogen", "MassUpload",
"VerifyHolding", "ConfirmHolding", "DeleteHoldingDir", "DeleteHoldingFile" };
base.OnActionExecuting(filterContext);
}
//......
[HttpPost]
public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
{
//code...
}
}
This system has actually worked out pretty well for us, but the problem for me has been that it feels messy. You have to define the methods one place, and override their authentication level elsewhere if necessary. If you change the method name you have to remember to change it elsewhere.
What I'd LOVE to be able to do is decorate the methods themselves with authentication specific attributes and do away the string-based definitions (or at least make them transparent and use List<string> dynamically or something). Here's an example of what I'm looking for...
[HttpPost]
[AdvisorAuthentication]
public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
{
//code...
}
Problem is that I can't find a good way to achieve this with attributes. I've tried creating subclasses of ActionFilterAttribute but they run after my BaseAuthController's override for OnActionExecuting. At that point it's too late in the game to add new methods to the string lists dynamically, and moreover I can't even seem to access the current controller instance from the attributes.
Maybe this whole idea is off base. Can anyone point me in the right direction? Thanks.
Final solution
First, I went ahead and deleted all of my special controllers except for BaseController - I had no use for them anymore. I moved the current special authentication code from BaseAuthController into BaseController. Next, I defined a series of attributes for each of my authentication states:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class BaseAuthAttribute : Attribute
{
public AuthLevels AuthLevel { get; protected set; }
public BaseAuthAttribute(AuthLevels level)
{
this.AuthLevel = level;
}
public override string ToString()
{
return string.Format("Auth Required: {0}", this.AuthLevel.ToString());
}
}
public class UnauthenticatedAccess : BaseAuthAttribute
{
public UnauthenticatedAccess()
: base(AuthLevels.Unauthenticated)
{
}
}
public class UserAccess : BaseAuthAttribute
{
public UserAccess()
: base(AuthLevels.User)
{
}
}
public class AdvisorAccess : BaseAuthAttribute
{
public AdvisorAccess()
: base(AuthLevels.Advisor)
{
}
}
public class AdminAccess : BaseAuthAttribute
{
public AdminAccess()
: base(AuthLevels.Admin)
{
}
}
Then in my BaseController I modified the OnActionExecuting to check the current auth level of the logged in user (if any) against the attribute. This is much cleaner than it was before! (Note: SessionUser and AuthLevels are custom objects for our project - you won't have those)
public partial class BaseController : Controller
{
/// <summary>
/// Override security at higher levels
/// </summary>
protected bool SecurityOverride = false;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
BaseAuthAttribute authAttribute = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
if (authAttribute == null) //Try to get attribute from controller
authAttribute = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
if (authAttribute == null) //Fallback to default
authAttribute = new UnauthenticatedAccess(); //By default, no auth is required for base controller
if (!SessionUser.LoggedIn
&& authAttribute.AuthLevel == AuthLevels.Unauthenticated)
{
SecurityOverride = true;
}
else if (SessionUser.LoggedIn
&& SessionUser.LoggedInUser.AuthLevel >= (int)authAttribute.AuthLevel)
{
SecurityOverride = true;
}
if (!SessionUser.LoggedIn && !SecurityOverride)
{
//Send to auth page here...
return;
}
else if (!SecurityOverride)
{
//Send somewhere else - the user does not have access to this
return;
}
base.OnActionExecuting(filterContext);
}
// ... other code ...
}
That's it! Now just put it to use like so...
[AdminAccess]
public class GrowerController : BaseController
{
public ActionResult Index()
{
//This method will require admin access (as defined for controller)
return View();
}
[AdvisorAccess]
public ActionResult Landing()
{
//This method is overridden for advisor access or greater
return View();
}
}
If I understood your question properly, you can implement your own custom attributes (not authorisation attributes) and in the overriden OnActionExecuting of the base controller, you can retrieve the custom attributes of the executing method and based on wich ones are defined you can take appropriate actions. So if a method has the [AdvisorAuthentication] you know that you need to check for those credentials before proceeding.
EDIT:
I don't have an example to point you to as this is something I have implemented in one of my projects. I have no access to that code now but here is an outline:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
IEnumerable<MyCustomAttribute> attributes = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<MyCustomAttribute>();
foreach (MyCustomAttributeobj in attributes)
{
switch(MyCustomAttribute.AttribType){
case MyCustomeAttribute.AdvisorAuthentication:
break;
case MyCustomeAttribute.AdminAuthentication:
break;
}
}
}
You can implement just one custom attribute MyCustomAttribute and have it accept a parameter to indicate which authorization type you want. Like that the use of the attribute becomes [MyCustomAttribute("MyCustomeAttribute.AdminAuthentication")]
You can create different Authorize attributes extending IAuthorizationFilter and FilterAttribute something like this
public sealed class AuthenticateAdvisorAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//advisor specific logic goes here
}
}
public sealed class AuthenticateAdminAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//admin specific logic goes here
}
}
And then you can apply those attributes wherever you require to controller classes/actions
as
[AuthenticateAdmin]
public class AdminController : Controller
{
}
[AuthenticateAdvisor]
public class AdvisorController : Controller
{
}

ASP.NET MVC 3 custom attribute with Ninject not executing

I must be doing something wrong because I have replicated many answers on this subject. My attribute binding is not being hit and I'm not sure why.
Controller.cs
[NatGeoUserAccessAuthorization]
[HttpGet]
public virtual ActionResult Teacher(string id)
{
Attribute/Fitler
public class NatGeoUserAccessAuthorizationAttribute : FilterAttribute{}
public class NatGeoUserAccessAuthorizationFilter : IAuthorizationFilter
{
private readonly IUsersService _usersService;
public NatGeoUserAccessAuthorizationFilter(IUsersService usersService)
{
_usersService = usersService;
}
public string QueryStringName { get; set; }
#region Implementation of IAuthorizationFilter
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!_usersService.HasUserAccess(filterContext.HttpContext.User.Identity.Name, filterContext.HttpContext.Request.QueryString[QueryStringName ?? "id"]))
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "action", "AccessDenied" }, { "controller", "Error" } });
}
}
#endregion
}
Global.asax
protected void Application_Start()
{
// NLog Custom Layouts
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("utc_date", typeof(UtcDateRenderer));
ConfigurationItemFactory.Default.LayoutRenderers.RegisterDefinition("web_variables", typeof(WebVariablesRenderer));
// Setup IoC Container
DependencyResolver.SetResolver(Bootstrap.Configure((kernel) =>
{
kernel.Bind<IDatabaseFactory>().To<DatabaseFactory<MySqlConnection>>().InRequestScope().WithConstructorArgument("connectionString", Config.Data.MySQLConnection);
ManagerBindings.Register(kernel);
ProviderBindings.Register(kernel);
RepositoryBindings.Register(kernel);
ServiceBindings.Register(kernel);
ValidationBindings.Register(kernel);
kernel.BindFilter<NatGeoUserAccessAuthorizationFilter>(FilterScope.Action, 0).WhenActionMethodHas<NatGeoUserAccessAuthorizationAttribute>();
kernel.Bind<IUserProfile>().To<UserProfile>();
kernel.Inject(Roles.Provider);
}));
// Custom Default Model Binder
ModelBinders.Binders.DefaultBinder = new ValidationModelBinder();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Bootstrap.cs
public class Bootstrap
{
public delegate void IocRegistrationDelegate(IKernel kernel);
public static IDependencyResolver Configure(IocRegistrationDelegate serviceBindings)
{
var kernel = new StandardKernel();
serviceBindings.Invoke(kernel);
return new NinjectDependencyResolver(kernel);
}
}
You are using an own bootstrapping mechanism that doesn't support filter bindings. Read the docu how to use the one that comes with Ninject.Mvc3 on http://github.com/ninject/ninject.web.mvc/wiki

Multiple attributes of same type fail when using Ninject's BindFilter<T>

As part of a identity-enabled authorization system, I'd like to use IAuhtorizationFilter and Attributes to restrict access to action methods in my controllers. I've got things working very well, partly due to help from the following resources:
Ninject Binding Attribute to Filter with Constructor Arguments
https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations
Custom Authorization MVC 3 and Ninject IoC
https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters
However, when I try to decorate an action method with more than one of my attributes, I get an exception as follows (sorry for the formatting):
[InvalidOperationException: Sequence contains more than one element]
System.Linq.Enumerable.Single(IEnumerable`1 source) +2691369
Ninject.Web.Mvc.FilterBindingSyntax.c__DisplayClass15`1.b__14(IContext ctx, ControllerContext controllerContext, ActionDescriptor actionDescriptor) in c:\Projects\Ninject\Maintenance2.2\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\FilterBindingSyntax\FilterFilterBindingBuilder.cs:379
Ninject.Web.Mvc.FilterBindingSyntax.c__DisplayClass12.b__11(IContext ctx) in c:\Projects\Ninject\Maintenance2.2\ninject.web.mvc\mvc3\src\Ninject.Web.Mvc\FilterBindingSyntax\FilterFilterBindingBuilder.cs:358
Ninject.Parameters.c__DisplayClass6.b__4(IContext ctx, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Parameters\Parameter.cs:60
Ninject.Parameters.Parameter.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Parameters\Parameter.cs:88
Ninject.Activation.Providers.StandardProvider.GetValue(IContext context, ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:97
Ninject.Activation.Providers.c__DisplayClass2.b__1(ITarget target) in c:\Projects\Ninject\Maintenance2.2\ninject\src\Ninject\Activation\Providers\StandardProvider.cs:81
...
Here is a very simplified version of my code that demonstrates the problem in an MVC3 app:
Attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class SampleAttribute : Attribute
{
private Guid typeId;
public bool IsAllowed { get; set; }
public SampleAttribute(bool IsAllowed)
{
this.IsAllowed = IsAllowed;
this.typeId = new Guid();
}
public override object TypeId
{
get
{
return (object)typeId;
}
}
}
Filter:
public class SampleFilter : IAuthorizationFilter, IMvcFilter
{
private bool isAllowed;
public bool AllowMultiple
{
get { return true; }
}
public int Order
{
get { return 0; }
}
public SampleFilter(bool isAllowed)
{
this.isAllowed = isAllowed;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if (!isAllowed)
throw new Exception("unauthorized");
}
}
Controller:
public class HomeController : Controller
{
[Sample(true)]
[Sample(false)]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
}
The controller method above works as expected if one or the other of the Sample attributes on the Index() method is removed. Having both in place, generates the exception. I realize that in this simplified example, there isn't a situation that would call for both attributes, but it's simply for illustration.
What am I missing?
This is a known issue of Ninject 2.2. Please use 3.0 instead.
https://github.com/ninject/ninject.web.mvc/blob/master/mvc3/ReleaseNotes.txt

Ninject multiple modules

I didnt think this would be a problem originally, but as I keep getting exceptions thought I would post here incase im being an idiot...
I have 2 module classes, one sets up NHibernate and one sets up MVC Controllers, now the problem I have is that I have something like below:
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionManager>().To<SessionManager>();
}
}
public class ControllerModule : NinjectModule
{
public override void Load()
{
Bind<SomeController>().ToSelf()
.WithConstructorArgument("sessionManager", Kernel.Get<ISessionManager>());
}
}
Whenever I try to use the controller, it just bombs out telling me that its having problems binding the sessionManager argument. I make sure the list has the Nhibernate module in before the Controller module when I create the kernel.
Is there anything immediately stupid in what im doing above?
Assuming:
public class SomeController : Controller
{
private readonly ISessionManager _sessionManager;
public HomeController(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public ActionResult Index()
{
return View();
}
}
The following should be sufficient:
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionManager>().To<SessionManager>();
}
}
public class ControllerModule : NinjectModule
{
public override void Load()
{
Bind<SomeController>().ToSelf();
}
}
and in Global.asax:
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NHibernateModule(),
new ControllerModule()
};
return new StandardKernel(modules);
}
}

Resources