ASP.NET MVC ActionFilter parameter binding - asp.net-mvc

If you have a model-bound parameter in an action method, how can you get to that parameter in an action filter?
[MyActionFilter]
public ActionResult Edit(Car myCar)
{
...
}
public class MyActionFilterAttribute : ActionFilterAttribute
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
//I want to access myCar here
}
}
Is there anyway to get myCar without going through the Form variables?

Not sure about OnActionExecuted but you can do it in OnActionExecuting:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// I want to access myCar here
if(filterContext.ActionParameters.ContainsKey("myCar"))
{
var myCar = filterContext.ActionParameters["myCar"] as Car;
if(myCar != null)
{
// You can access myCar here
}
}
}
}

Related

How to write an action filter for all controllers

Here is a sample action filter. We know that when we write an action filter then we need to decorate the controller with an attribute like this, to use it for any controller.
I like to know whether there is any way to write an action filter which will work for all controllers in way that I do not need to decorate all the controllers with an action filter attribute. Any ideas?
[LogActionFilter]
public class HomeController : Controller
{}
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", 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");
}
}
public class LogActionFilterAttribute : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", 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");
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalFilters.Filters.Add(new LogActionFilterAttribute());
}
}
For your scenario you can just make a Custom BaseController and put your [LogActionFilter] attribute on Custom Basecontroller and inherit all your Controllers from Custom Basecontroller as shown below :
[LogActionFilter]
public class MyBaseController : Controller
{
}
public class MyOtherController : MyBaseController //<----instead of using Controller as base use MyBaseController as base class
{
public ActionResult Index()
{
// ...
}
}
The advantage of this approach is that you have to put your custom [LogActionFilter] attribute only at one place i.e. only on Custom BaseController.
If you're already subclassing from a base controller, you don't need a filter attribute or to register anything. You can just override the desired methods, e.g.,
public class BaseController : Controller {
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
ViewBag.SomeValue = "glorp frob blizz nunk";
// Access the controller, parameters, querystring, etc. from the filterContext
base.OnActionExecuting(filterContext);
}
}
Then, every controller that subclasses from it will run that method:
public sealed class GlorpController : BaseController {
public ActionResult Index() => View();
}
The view will quite happily see the ViewBag.SomeValue value.

How do I pass variables to a custom ActionFilter in ASP.NET MVC app

I have a controller in my MVC app for which I'm trying to log details using a custom ActionFilterAttribute, by using the onResultExecuted method.
I read this tutorial to understand and write my own action filter. The question is how do I pass variables from the controller to the action filter?
I want to get the input variables with which a controller is called. Say, the username/user ID.
If (in some situations) an exception is thrown by any controller method, I would want to log the error too.
The controller -
[MyActionFilter]
public class myController : ApiController {
public string Get(string x, int y) { .. }
public string somemethod { .. }
}
The action filter -
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void onActionExecuted(HttpActionExecutedContext actionExecutedContext) {
// HOW DO I ACCESS THE VARIABLES OF THE CONTROLLER HERE
// I NEED TO LOG THE EXCEPTIONS AND THE PARAMETERS PASSED TO THE CONTROLLER METHOD
}
}
I hope I have explained the problem here. Apologies if I'm missing out some basic objects here, I'm totally new to this.
Approach - 1
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
}
}
Action Method
[MyActionFilter]
public ActionResult Index()
{
ViewBag.ControllerVariable = "12";
return View();
}
If you pay attention to the screenshot, you can see the ViewBag information
Approach - 2
Action Filter
public class MyActionFilter : ActionFilterAttribute
{
//Your Properties in Action Filter
public string Property1 { get; set; }
public string Property2 { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
Action Method
[MyActionFilter(Property1 = "Value1", Property2 = "Value2")]
public ActionResult Index()
{
return View();
}
I suggest another approach, and it is passing parameters to Action Filter as constractor.
[PermissionCheck(Permissions.NewUser)]
public ActionResult NewUser()
{
// some code
}
Then in the ActionFilter:
public class PermissionCheck : ActionFilterAttribute
{
public Permissions Permission { get; set; }
public PermissionCheck(Permissions permission)
{
Permission = permission;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (/*user doesn't have that permission*/)
{
filterContext.Result = new RedirectToRouteResult
(
new RouteValueDictionary
(
new {
controller = "User",
action = "AccessDeny",
error = "You don't have permission to do this action"
}
)
);
base.OnActionExecuting(filterContext);
}
}
}
Which Permissions is an ENUM like:
enum Permissions {NewUser, Edit, Delete, Update, ...}

how to pass a parameter from action to ActionFilterAttribute code

i have ActionFilterAttribute like the following
class MyCustomRouteConstraint : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (something == 1) //i know there is no something variable
{
// do something
}
base.OnActionExecuting(filterContext);
}
}
as you can see in my code there is no variable named as something.
but i want to use the variable named as something in my action.
public ActionResult Index()
{
int something = 1;
return View();
}
public ActionResult About()
{
int something = 2;
return View();
}
public ActionResult Contact()
{
int something = 1;
return View();
}
I think what you need is OnActionExecuted. I have not tested.
class MyCustomRouteConstraint : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var something = Convert.ToInt32(filterContext.RouteData.Values["something"]);
if(something == 1)
{
//do something
}
base.OnActionExecuting(filterContext);
}
}
OnActionExecuting is called by the ASP.NET MVC framework BEFORE the action method executes. So it doesn't make sense to initialize your something variable in the action body. But still if you are going to override some method which is called AFTER the action method executes you can probably use ViewBag to init the variable in the controller and then get its value using filterContext.
You use ViewBag for this as well.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.SommeVariable = "Test";
}
And in your action
public ActionResult Index()
{
var variable = ViewBag.SommeVariable;
return View();
}
I use structuremap dependency injection to do that. See here some snippets:
namespace something.Infrastructure.ActionFilters {
public class PlatformAuthorizeAttribute : AuthorizeAttribute
{
public IRepository<User> UserRepo { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (WebSecurity.Initialized && filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (filterContext.HttpContext.User.IsInRole("Banned"))
{
WebSecurity.Logout();
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"Controller", "Home"},
{"Action", "Banned"},
{"Area", ""}
});
}
UserRepo.Dispose();
UserRepo = null;
}
}
}
}
And then initialize the following mapping in your IoC structuremap class:
public static class IoC
{
public static IContainer Initialize()
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
x.SetAllProperties(pset =>
{
pset.WithAnyTypeFromNamespace("something.Infrastructure.ActionFilters");
pset.OfType<IRepository<User>>();
});
});
return ObjectFactory.Container;
}
}
I think this is the cleanest way to provide your action filters with properties. Note that in my example I used a custom AuthorizeAttribute. You will need to change this to ActionFilterAttribute and use the OnActionExecuting method override to access the properties.
Use HttpContext.Items
HttpContext.Items["something"] = 1;
In ActionFilter, you can access it as:
var something = (int)filterContext.HttpContext.Items["something"];
However, your example of action filter is OnActionExecuting; this will execute before any of your Index/About actions executes. So you should initialize 'something' somewhere else as per your needs (for example, inside controller's OnActionExecuting method).

Get Session from HttpActionContext

I'm trying to create a permission attribute to configure in each action of my controllers so this custom attribute should take the sessionId from the user.
My code is like that:
public class PermissionChecker: ActionFilterAttribute
{
private int _permissionId { get; set; }
private IUserSelectorService _userService { get; set; }
public PermissionChecker(int permissionId)
{
_permissionId = permissionId;
_userService = new UserSelectorService();
}
public PermissionChecker(int permissionId, IUserSelectorService userService)
{
_permissionId = permissionId;
_userService = userService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (_userService.HasPermission(_permissionId, /* here I must pass the session["Id"]*/)){
base.OnActionExecuting(actionContext);
return;
}
throw new HttpException(401, "Unauthorized");
}
}
Use this
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
var id = filterContext.HttpContext.Session["Id"];
}
}
EDIT
Given the fact that you're using MVC 4 and you don't have
public override void OnActionExecuting(ActionExecutingContext filterContext)
Try using
System.Web.HttpContext.Current.Session
if you are trying to access using ActionFilterAttribute then OnActionExecting event it wont give the accessibility of HttpContext with System.Web.Http.
Instead of that If you are trying to access using System.Web.Mvc it will provide you the current session with onActionExecting event with help of ActionExecutingContext class.

Calling FilterAttribute's OnActionExecuting before BaseController's OnActionExecuting

I have a BaseController in which I put in some data in the ViewData collection by overriding OnActionExecuting.
Now i have an Action in a ChildController that doesn't need that view data.
For that purpose I created an DontPopulateViewData ActionFilterAttribute that sets a bool on the BaseController that prevents the BaseController from populating the viewdata.
Problem: the ActionFilters OnActionExecuting method is called after the one in BaseController and not before.
Will ActionFilters always be called before overridden OnActionExecuting in base controllers and is there a way to get around this?
In addition to what Marwan Aouida posted and suggested (using an ActionFilter on the base class), I don't think you're going to be able to create an ActionFilter that executes before the OnActionExecuting() overload on the base class. The following code:
[MyActionFilter(Name = "Base", Order = 2)]
public class MyBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
Response.Write("MyBaseController::OnActionExecuting()<br>");
base.OnActionExecuting(filterContext);
}
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
requestContext.HttpContext.Response.Write("MyBaseController::Execute()<br>");
base.Execute(requestContext);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
Response.Write("MyBaseController::OnActionExecuted()<br>");
base.OnActionExecuted(filterContext);
}
}
public class MyActionFilter : ActionFilterAttribute
{
public string Name;
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write("MyActionFilter_" + Name + "::OnActionExecuted()<br>");
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write("MyActionFilter_" + Name + "::OnActionExecuting()<br>");
base.OnActionExecuting(filterContext);
}
}
public class MyTestController : MyBaseController
{
[MyActionFilter(Name = "Derived", Order = 1)]
public void Index()
{
Response.Write("MyTestController::Index()<br>");
}
}
produces this output:
MyBaseController::Execute()
MyBaseController::OnActionExecuting()
MyActionFilter_Derived::OnActionExecuting()
MyActionFilter_Base::OnActionExecuting()
MyTestController::Index()
MyActionFilter_Base::OnActionExecuted()
MyActionFilter_Derived::OnActionExecuted()
MyBaseController::OnActionExecuted()
The ActionFilterAttribute class has a property called "Order" which you can use to set the order in which the Action Filters are executed.
In your case you have to set the order of the Filter Attribute in the BaseController to 2 and the Filter Attribute in the DerivedController to 1:
[MyFilter(Order=2)]
public class BaseController:Controller
{
public ActionResult MyAction() {
}
}
[MySecondFilter(Order=1)]
public class DerivedController:BaseController
{
public ActionResult AnotherAction() {
}
}
Read this for more infos: http://msdn.microsoft.com/en-us/library/dd381609.aspx
Note: I didn't test this.

Resources