I have several action methods that get initiatated on a each Action on the page.
One of the Property asscoiated with this class has an custom-attribute defined, this inturn calls an method(action=method) defined within the same controller class.
But this action-method shouldn't be called on all actions , How to check before redirecting to this specific action ?
You can read from filter context or controller context object the value of current action from RouteData like this:
public class MyAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var action = filterContext.RouteData.Values["action"];
if(action == "xxx")
...// then
base.OnResultExecuting(filterContext);
}
}
Related
I am required to record user action and I don't want to have code in every method is every controller so would it make sense to somehow do this in the basecontroller ? OR is there a better way?
public class BaseController : Controller
{
protected ILogger logger;
public BaseController(ILogger<BaseController> logger)
{
this.logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
//How do I get the current method being called?
//How can I pass in additional parameters?
//How can I get the user?
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
}
There are many ways to do that.
First: You could create your own base controller and implement OnActionExecution as you did. See the sample bellow to get information from ActionExecutingContext.
If you go this way, every controller that inhirits from this base controller will get the implementation of the logger because you are overriding OnActionExecuting (that applies to all actions of your controller).
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
string controllerName = context.ActionDescriptor.ControllerDescriptor.ControllerName
//How do I get the current method being called?
string actionName = context.ActionDescriptor.ActionName;
//How can I pass in additional parameters?
foreach (var parameter in context.ActionParameters)
{
var parameterKey = parameter.Key;
var parameterValue = parameter.Value;
}
//How can I get the user?
var user = this.User; // IPrinciple instance, explore this object
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
Second: On the other hand, you can use ActionFilters which is a class that inhirits from ActionFilter class and do the same implementation on this classe overriding the OnActionExecuting. Then you can decorate your controllers with this attribute to make the logger. Given it is an attribute, you have to define the name fo the class with a sufix Attribute and use without it. For sample:
public class LoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// same code above
}
}
[Logger]
public class CustomerController : Controller
{
// actions code...
}
Third: Use the same action filter class and instead of applying on all classes you want, you define it as a global action filter and it will be applied to all controllers. You have to define it on GlobalFilter and if you are using the default template of asp.net mvc, you can define it on the FilterConfig.cs, for sample:
filters.Add(new LoggerAttribute());
For getting controller & action names you can use ActionDescriptor of ActionExecutingContext
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var descriptor = filterContext.ActionDescriptor;
var actionName = descriptor.ActionName;
var controllerName = descriptor.ControllerDescriptor.ControllerName;
......
base.OnActionExecuting(filterContext);
}
Regarding User information: controller initialisation will occur before authorisation takes place. So all of yours controllers will be created before any OnAuthorization takes place.
Approach to deal with these situations is to use Action Filters. The Authorize Attribute is fired early than controller initialisation occur.
Have a look this articles:
How to get controller and action name in OnActionExecuting?
How do I get the action name from a base controller?
Getting User Identity on my base Controller constructor
I have an action filter which (among other things), adds stuff to the RouteData. The value, however, is not picked up by the parameter in my action method. Any ideas why?
Action Filter:
public class SomeFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var someData = new SomeClass();
//do stuff
filterContext.RouteData.Values["someData"] = someData;
}
}
Action Method:
[SomeFilter]
public ViewResult SomeActionMethod(SomeClass someData)
{
//someData is null here
}
Please note that the following line inside my action method does return something the data saved into it in action filter:
SomeClass isNotNull = RouteData.Values["someData"] as SomeClass;
Anyone knows why?
The filter is attached to the action (method). Hence by the time the filter is run, the values for the parameters have already been chosen. Imagine the situation if what you asked worked:
[SomeFilter]
public ViewResult SomeActionMethod()
{
// ....
}
public ViewResult SomeActionMethod(SomeClass someData)
{
// .....
}
You reference http://mysite.com/mycontroller/SomeActionMethod with no query parameter. So then it should call the first action. But if your filter were to do what you wanted, after it ran, it should call the second action. But that one DOESN'T have the filter, so it should call the first. And round & round.
Here's an article that covers how to modify the Parameter values from an Action Filter:
http://haacked.com/archive/2010/02/21/manipulating-action-method-parameters.aspx/
I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?.
For example:
Inside the constructor I need to initialize an object with an dynamic value, in some cases I don't want to do it and in that case I want to redirect to some other place.
At the same way the rest of the constructor will not be executed neither the "original following action".
How can I do it?
Thank you
EDIT #1
Initially I used:
public override void OnActionExecuting(ActionExecutingContext filterContext)
There I could redirect to some other controller/action/url, but later in time, I needed to change my controller, where I initialize an variable in his constructor and have some code that really needs to redirect the request :P
I need this also because the OnActionExecuting executes AFTER the controller constructor.
And in my logic, the redirect needs to be done there.
Performing redirects inside the controller constructor is not a good practice because the context might not be initialized. The standard practice is to write a custom action attribute and override the OnActionExecuting method and perform the redirect inside. Example:
public class RedirectingActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (someConditionIsMet)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "someOther",
action = "someAction"
}));
}
}
}
and then decorate the controller which you would like to redirect with this attribute. Be extremely careful not to decorate the controller you are redirecting to with this attribute or you are going to run into an endless loop.
So you could:
[RedirectingAction]
public class HomeController : Controller
{
public ActionResult Index()
{
// This action is never going to execute if the
// redirecting condition is met
return View();
}
}
public class SomeOtherController : Controller
{
public ActionResult SomeAction()
{
return View();
}
}
I want to get a value from the user session and display it in the site.master file. How can I do this so that every view page has this value? Do I have to place ViewData["MyValue"] in every controller action? Is there a global way of doing this in one place so I don't have to have the same code in every controller action?
You could write an action filter attribute and decorate your controller with it:
public class CustomFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.Controller.ViewData["MyValue"] = "some value";
}
}
And then decorate the controller with this attribute:
[CustomFilter]
public class MyController: Controller
{
// actions
}
This will ensure that ViewData["MyValue"] will be set on all action belonging to this controller.
<%= Session["MyValue"] %> in the master page
I have a controller that is all ajax calls, so I want to verify it's an ajax call once on the page before it calls the controller action so that if it's not an ajax call I can redirect to a home or error page. Is this possible? I know I can put in the constructor class, but the request object is null at that point.
You can use an ActionFilter. It's designed to accomplish this task:
public class MyActionFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
// will get executed when the decorated action runs
}
}
// Action method:
[MyActionFilter]
public ActionResult Index(int id) {
// ...
}