I have 2 controllers Home with
public class HomeController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do some irrelevant stuff
base.OnActionExecuting(filterContext);
}
public ActionResult Index()
{
return View();
}
}
and Service with
public ActionResult Confirm()
{ return RedirectToAction("Index", "Home");}
And one ActionFilterAttribute with OnActionExecuting method
public class InvitationModeAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do some stuff
base.OnActionExecuting(filterContext);
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new InvitationModeAttribute());
}
}
When I go to localhost/Service/Confirm , OnActionExecuting is fired, but then when RedirectToAction is called, OnActionExecuting is not fired.
How can I catch this after RedirectToAction?
Thanks
Refer this For More clarity
First of all
Remove OnActionExecuting method in controller level
public class HomeController : Controller
{
[InvitationModeAttribute]
public ActionResult Index()
{
return View();
}
}
2nd Controller
public class ServiceController : Controller
{
[InvitationModeAttribute]
public ActionResult Confirm()
{
return RedirectToAction("Index", "Home");
}
}
From MSDN
Scope of Action Filters
In addition to marking individual action methods with an action
filter, you can mark a controller class as a whole with an action
filter. In that case, the filter applies to all action methods of that
controller. Additionally, if your controller derives from another
controller, the base controller might have its own action-filter
attributes. Likewise, if your controller overrides an action method
from a base controller, the method might have its own action-filter
attributes and those it inherits from the overridden action method. To
make it easier to understand how action filters work together, action
methods are grouped into scopes. A scope defines where the attribute
applies, such as whether it marks a class or a method, and whether it
marks a base class or a derived class.
Related
I have a BaseController which is like below.
public class BaseController : Controller
{
public string BDynamicConnectionString { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Getting dynamic connectionstring
this.BDynamicConnectionString = GetDynamicConnetionString(this.BCCompanyId, this.BCCompanyIdentifier);
}
}
I have inherited this base controller in my controller as below.
public class TestController : BaseController
{
private ClassroomBL objClassroomBL;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
this.objClassroomBL = ClassObjects.ClassroomBLObject(BDynamicConnectionString);
}
public ActionResult FetchDefaultStatuses()
{
this.objClassroomBL.FetchDefaultStatuses(145);
return View();
}
}
As it is visible I have OnActionExecuting(ActionExecutingContext filterContext) on both BaseController and TestController.
Actually Why I did it like this is I am managing many things in base controller which are needed in each controller inherited from this.
But now I need something like OnActionExecuting(ActionExecutingContext filterContext) on particular controller because I have to initialize object before calling action and object which I have to initialize it uses a property which is initialized in BaseController.
If I am adding OnActionExecuting(ActionExecutingContext filterContext) on both then first, controller which is inheriting BaseController on this event fires before the BaseController. I know this happening because of OOPS concept.
Is anything else I can add on Controller which fires after Basecontrollers OnActionExecuting but before action execution of particular controller .
Thanks!
Just use following in the first line of overrided method:
base.OnActionExecuting(filterContext)
See also base keyword.
So what I have is a base controller that the following [Route] definition
[Route("{application}/api/[controller]")]
public class BaseController
{
}
All of my current controllers inherit from BaseController.
What I am trying to achieve is that two different application can call my controllers and my code to be aware of what 'application' is calling it.
Application 1 should be able to call /Application1/Api/MyController
Application 2 should be able to call /Application2/Api/MyController
and both requests should go to the same controller but my code should be aware of which application called it.
I thought about having some sort of Middleware and then work out the application from the Request.Path, and then store it in something like HttpContext.Current.Items but that doesn't seem like the correct way to do it.
My personal preference here would be to pass the value as an HTTP header rather than a route parameter, especially if you want it everywhere. It means you don't need a Route attribute and a different URL per application. Using a custom ActionFilterAttribute, there's a bunch of ways you can pass this detail into your action. For example:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Request.Headers.TryGetValue("Application", out var values))
{
// Method 1: This allows you to specify a parameter on your action
context.ActionArguments.Add("application", values.First());
// Method 2: This adds the value into the route data
context.RouteData.Values.Add("Application", values.First());
// Method 3: This will set a property on your controller
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = values.First();
}
}
base.OnActionExecuting(context);
}
}
And apply it to action methods or your controller:
[Application]
public class FooController : Controller
{
}
Method 1 Usage:
public IActionResult Index(string application)
{
// do something with the parameter passed in
}
Method 2 Usage:
public IActionResult Index(string application)
{
var application = (string)RouteData.Values["Application"];
}
Method 3 Usage:
First, create a base controller that contains the property:
public abstract class BaseApplicationController : Controller
{
public string Application { get; set; }
}
Then make sure your controller inherits from it:
[Application]
public class FooController : BaseApplicationController
{
}
Now you can access the property on your controller:
public IActionResult Index(string application)
{
var application = this.Application;
}
Bonus Method 4:
As an aside, you could use this method to use the URL route value, using the base controller from method 3, modify the attribute to look like this:
public class ApplicationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.Controller is BaseApplicationController baseController)
{
baseController.Application = (string)context.RouteData.Values["application"];
}
base.OnActionExecuting(context);
}
}
Add a route attribute to your controller:
[Route("{application}/api/[controller]/[action]")]
And now you should have the property value on the controller set.
You could move the route template into action and then each action would technically be aware of its caller context by your proposed convention:
[Route("api/[controller]")]
public class YourController : BaseController
{
[HttpGet("{application}")]
public IActionResult Get(string application)
{
if (application == "Application1")
{
...Application1 called
}
if (application == "Application2")
{
...Application2 called
}
...
}
}
Of course, this is your proposed convention and it is not enforced through some custom application authentication in any way so you will have to trust that your callers will correctly identify themselves through this convention.
Another approach, could be to have a base class variable and set that after inspecting the route.
[Route("{application}/api/[controller]")
public class BaseController: Controller
{
protected string CallingApp { get; set; }
public override void OnActionExecuting(ActionExecutingContext ctx)
{
CallingApp = ctx.RouteData.Values["application"];
base.OnActionExecuting(ctx);
}
}
I have worked in MVC on .Net 4 and I had my action filters overridden in the controller class. That way I can have multiple controllers, each with a unique OnActionExecuting method.
Now I'm working on Dotnet Core 2.1 and things are a little different so I can't get the same methodology to work.
I have a controller for an API that inherits ControllerBase and then another controller for serving pages, that one inherits Controller.
I created my action filter class that derives from ActionFilterAttribute.
I then registered my action filter by using this line:
services.AddMvc(config => { config.Filters.Add(typeof(MyActionFilter)); })
The problem is that the filter now applies to the API controller as well as the pages controller. Is there a way the action filter can be applied to specific controllers only?
I used this page as a reference for my initial action filter implementation.
I know that inside the OnActionExecuting method I can use context.Controller.GetType() to compare it to the types of my individual controllers, but I would prefer to avoid that, and instead specify which controllers should use which action filters.
Thanks in advance.
services.AddMvc(config => { config.Filters.Add(typeof(MyActionFilter)); })
The above source code is used when you want to apply the global filter that mean every API will have the affected.
So to specific particular Controller you just simply add [MyActionFilter] atribute to you controller
Example:
[MyActionFilter]
public class HomeController : Controller {
public ViewResult Index(){..}
}
First of All Register Your filter in your startup.cs
services.AddScoped<"---FilterNameHere----">();
Put Your Filter in controller like
[ServiceFilter(typeof("----FilterNameHere---"))]
public class HomeController : ControllerBase
{
public IActionResult Index()
{//do your code here}
}
Was looking for an answer too. Both above confused me. Went to the page of the documentation...
Two ways of doing this in a clean way.
An action filter for all actions of all controllers.
public class YourActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new System.NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext context)
{
throw new System.NotImplementedException();
}
}
And in startup do
services.AddControllers(c => { c.Filters.Add(new YourActionFilter()); })
An Attribute that you want to use only on specific actions which implements TypeFilterAttribute and has an private class that implemements an action filter.
public class YourActionFilterAttribute : TypeFilterAttribute
{
public YourActionFilterAttribute() : base(typeof(TheActionFilter)) { }
private class TheActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new System.NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext context)
{
throw new System.NotImplementedException();
}
}
}
Now you only need to add it to a whole controller or to a specific action
[YourActionFilter]
public class HomeController : ControllerBase
{
[YourActionFilter]
public IActionResult Index()
{//do your code here}
}
I have implemented my own custom Authorize attribute.
The attribute is applied both at the controller level and at the action level.
Here is an example of what I need to do:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
Currently if a user has the Administrator Role but not the AdvancedUsers role, he cannot execute "Administrative Task".
How can I change this behavior to perform a security check at the action level even if the user is not authorized at the controller level?
For the moment, the only solution I can think about is to implement 2 attributes: one for securing controllers, another for securing actions. Then I would play with the Order property to execute the one at the action level first.
However, I would prefer a solution with a single attribute if possible.
Use built-in [OverrideAuthorization]:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[OverrideAuthorization]
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
public ActionResult SomeOtherAction()
{
return View();
}
}
OverrideAuthorization Attribute is available for MVC 5 (at least) and up. Once you decorate the Action with it, also decorate with the new Role and that will take effect over the Controller level Role.
This should not be possible. Imagine the logic which MVC uses with the authorization filters.
When the controller is determined - check if there is an authorization filter that applies to that controller and execute it.
When the action is known - do the same for the action.
In all cases a fail in authorization would short-circuit the pipeline.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions.
When you mark an action method with the Authorize attribute, access to that action method is restricted to users who are both authenticated and authorized.
//[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
{
[ClaimsAuthorize(Roles ="Administrators", "Role2","Role3")]
public ActionResult AdministrativeTask()
{
return View();
}
}
OR you can override your authorization at controller level ,
Create a new OverrideAuthorizeAttribute attribute.
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
and you can use this attribute to override your controller level autorization.
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
[OverrideAuthorizeAttribute(Roles ="xxxx")] // This role will override controller
//level authorization
public ActionResult SomeOtherAction()
{
return View();
}
}
You need two authorization attributes - a base one with all authorization logic, and a second one, derived from the base attribute, that is only used to override the base attribute.
Example authorization attributes:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
protected bool _canOverride = true;
//...custom authorization code goes here.....
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//Don't authorize if the override attribute exists
if (_canOverride && actionContext.ActionDescriptor.GetCustomAttributes<OverrideClaimsAuthorizeAttribute>().Any())
{
return;
}
base.OnAuthorization(actionContext);
}
}
public class OverrideClaimsAuthorizeAttribute : ClaimsAuthorizeAttribute
{
public OverrideClaimsAuthorizeAttribute ()
: base()
{
_canOverride = false;
}
}
In the base authorization attribute we are saying to go ahead and authorize as normal, as long as the OverrideClaimsAuthorizeAttribute doesn't exist. If the OverrideClaimsAuthorizeAttribute does exist, then only run the authorization on classes where _canOverride is false (ie the OverrideClaimsAuthorizeAttribute class itself).
Example usage:
[ClaimsAuthorize(Roles = "AdvancedUsers")]
public class SecurityController : Controller
{
//Ignores the controller authorization and authorizes with Roles=Administrators
[OverrideClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdministrativeTask()
{
return View();
}
//Runs both the controller and action authorization, so authorizes with Roles=Administrators AND Roles=AdvancedUsers
[ClaimsAuthorize(Roles = "Administrators")]
public ActionResult AdvancedAdministrativeTask()
{
return View();
}
//authorizes with controller authorization: Roles=AdvancedUsers
public ActionResult SomeOtherAction()
{
return View();
}
}
Check this previous question. (check #AndyBrown answer, case 2)
For a simple way you might also try adding (
[AllowAnonymous]) to override the controller
[Authorize]
then add a new custom filter to check for your logic for this particular action. Or you can add the code that checks for the role just inside it.
Out of pure curiosity, is it possible to access the current controller from a static context while it is being executed with the current HttpRequest/Action?
No, this is not possible from a static context because many different controllers could be executing at some given point of time for multiple concurrent requests.
I don't know of a way to do it statically but what I do for this while handling some session/authentication management I have all my controllers inherit from a custom BaseController class that inherits from the System.Web.Mvc.Controller class. In the Base Controller class I override the OnActionExecuted method.
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Your logic here
base.OnActionExecuted(filterContext);
}
}
public class HomeController : BaseController
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
}