What is the best way to create custom OnActionExecuted code for all HTTP GET actions in a .NET MVC application?
Would you create an ActionFilter, or create a base controller, and in either of these approaches is it possible to fire the action filter only on GET requests?
My initial thinking is a base controller written as follows, but is this the best way, or am I missing something?
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (Request.HttpMethod == "GET")
{
...
}
}
You code is good. I would use:
if (string.Equals(Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
Also if you would like to create few ActionFilters for 'GET' request only you may create common base ActionFilter class and then derive all concrete action filters from it.
Controller itself is a filter so you can use the controller or you can go for a separate class/filter. You have to analyze which one suits for you. By putting the logic in the controller you may lose unit testing so if testing/SOC are important concerns then I may suggest go for a separate class that encapsulates the logic. Also, you can avoid code duplication if you have two different base controllers in an application (rarely).
The best way to do this turned out to be neither using base controller or custom action filter declared on actions. It's best to globally register the action filter using a controller factory, and requires neither inheriting from a base controller nor adding the action filter on ever controller/action. The action filter is assigned to the Controller ActionInvoker in a custom DefaultControllerFactory derivation declared in global.asax.
This blog post was useful in implementing this approach.
Related
I have a base controller where most of my api logic sits. I want to standardise the api verbs and I want this base controller to handle most request by default unless I inherit from this controller and override the specific action.
Given a path like this: "/api/Socks/Get?apiKey=1" I am able to do something like this:
[Route("api/[controller]/[action]")]
public class RestDbApiController : Controller
{
[HttpGet]
public virtual async Task<JsonResult> Get(string apiKey = null) {
.....
public class SockController : RestDbApiController
{}
This works okay - ie. the request is routed the action on the base controller. The issue is that I have to declare the SockController, otherwise the request will not route.
I would like to be able to route all requests to "/api/xxxxx" to the base controller without having to declare any other controllers. Please let me know if there is any way to do this.
Why do I want to do this?
I'm trying to make a generic controller with external scripted definitions. Depending on the controller name it would read the script from a similarly named script file. I want to be able to just add the script file to a directory and have it work just like that without having to make any declarations in the code
This isn't really practically possible. You can technically just bind a wildcard route to the controller, but that will then swallow everything. In other words, all your API routes would hit this base controller forever, and you then have to basically set up your own routing infrastructure inside that base controller to reroute requests to the right controller. In case it's not obvious, that's an extremely bad idea.
The best thing to do is actually the thing you don't want to do: actually define the derived controller. While you may look at this like it's extraneous, it's actually not at all. It serves to make your code self-documenting. It's clear that this is a controller that deals with "socks", but it doesn't have any specific logic of it's own. That's perfectly okay.
I agree with #Chris, but if you want to have only one controller, you could omit the "controller name" part from the routing definition. For example, the following requests will be mapped to below action method
/api/Socks/Get?apiKey=1
/api/OtherSocks/Get?apiKey=1
and name parameter will be filled as "Socks", "OtherSocks" respectively:
public class RestDbApiController : Controller
{
[HttpGet]
[Route("api/{name}/[action]")]
public virtual async Task<JsonResult> Get(string name, string apiKey = null)
{
...
}
}
In an intranet web application at my company, numerous operations have a granular, custom security system which is used in each action/http method in our MVC controllers. Basically there are two enums; one is a set of actions that can be performed (this is extremely granular; practically every possible action has a corresponding value in the enum) and one is a set of our subcompanies. For the context of this question, I will call these enums Action and Company.
Each user in our system is associated to one Company and zero or more Actions. Inside each method in our controllers, somewhere along the way there is a check for if the current user has the right Action and Company value to be using that feature. Every controller has a "UserHelper" injected into it which contains the Company and list of Actions the authenticated user is associated with.
Approaching it this way, there is a lot of code duplication in that every method is doing its own check on these enum values and reacting to violations when necessary. I am hoping to reduce this all to a System.Attribute or System.Web.Mvc.AuthorizeAttribute which we can put on controllers or methods to automatically handle violations in a uniform way and not have to check for it within the methods. Something akin to:
public class MyController : Controller
{
[RequireActionAndCompanyAttribute(Action = Action.MyMethod, Company = Company.AbcInc)]
MyMethod()
{
// do stuff, but don't bother checking for the security values
}
}
As mentioned, I am assuming I can inherit from System.Attribute or System.Web.Mvc.AuthorizeAttribute for this purpose. However, I'm struggling. I'm not sure how to adapt AuthorizeAttribute to use our own internal security implementation (based on Action and Company) rather than one of the ASP.NET Membership systems. And the plain old System.Attribute seems so vague that I'm thinking it wasn't designed for this kind of use at all. I'm also not sure how I'm supposed to pass anything to the methods in the attribute. When I put the attribute on a method, I just want to specify what Action and Company are required to continue, like in the code snippet above. But then, how do I pass the user's actual values for these into the attribute's validation method?
Use a custom attribute inherited from ActionFilterAttribute instead of the AuthorizeAttribute. You can inject your UserHelper in this class and override the OnActionExecuting method and set the Result property of the context when your condition isn't met.
Let's suppose I have a layer of abstract controllers, which delegates the request to its child controller class, until it reaches the implementation.
Think of it like a pipeline of controllers, that the request must go through, and includes caching the responses, authorizing and authenticating the user, validating the input and output data, handling repository access, etc.
My leaf class (the last child of the hierarchy), may have the following signature:
public class SeasonsController : DefaultPersistenceRestController
<int, Season, SeasonPutDTO, SeasonPostDTO, SeasonQueryData> {
/** Controller implementation here **/
}
The base classes have a lot of reusable code located in one module, this is good and has helped me a lot when changing the logic of my controllers at a global level.
Now, suppose SeasonsController need to call EpisodesController, for irrelevant reasons.
The call would be like this:
EpisodesController episodeController = new EpisodesController();
//Do something with EpisodesController
The problem is that I don't want EpisodesController to be accessed from the outside, such as client's request. ASP.NET automatically identifies controllers and creates a public endpoint for them, such as http://localhost:80/episodes.
I created EpisodesController because it uses a lot of logic from the controller's base classes, but I intend to use it internally.
I can desactivate authentication, authorization, cache and all other stuff that will be useless if a controller is used in this way, so that's not a problem.
However, I cannot manage to prevent ASP.NET to ignore my EpisodesController class, and to not consider it like a controller.
Is there an attribute or annotation maybe that will tell the compiler to do this? Maybe some modification in Web.config?.
Also note that I don't want to change EpisodesController's class name to another name, as it is really a controller, but an internal one.
You could try to use the IgnoreRoute extension method. Or you could try the internal as suggested by beautifulcoder. If it's in another assembly (and you can modify it) you could also make it visible to other assemblies with InternalsVisibleToAttribute.
Although to be honest, using one controller within another controller doesn't seem right to me. I would try and refactor you common functionality to services/helpers, then you could probably also make your EpisodesController into a simple service. Composition over inheritance and all that :)
If you make a controller public it will be accessible. From what I understand, you can change it to protected or internal.
In my MVC3 C# project, I have been looking at ways to construct a navigation menu in controller code and pass that to the master page. (i want to initialize the menu in controller so I can do some authorization and roles checking, etc...)
So far, I found several answers on here taht all seem to require overriding the OnActionExecuted method of a BaseController (from which all other controllers extend)
Example 1(see accepted answer): How to create a strongly typed master page using a base controller in ASP.NET MVC
Example 2: "Security aware" action link?
But wy not just put that same code in the Base Controller's constructor?
If all your controllers inherit from the same base controller, there is no reason why you could not do it this way.
The advantage of an ActionFilterAttribute is that it allows you to insert your logic on any controller by simply decorating it with the attribute, allowing you the freedom of inheriting from any base class you choose.
I wondered if/how I can override the default [Authorize] behavior in ASP.NET MVC. I know that I can create a new Action Filter, make my own attribute and so forth; I am merely interested if I can simply change the [Authorize] behavior and replace its workings with my own code?
Edit: Guys and Girls. I appreciate your input but as I wrote, I am not looking to introduce a new [XYZAuthorize] Attribute. I'm aware of how to do this. I want to keep the [Authorize] notation but just change how it works.
You can subclass the AuthorizeAttribute filter and put your own logic inside it.
Let's see an example. Let's say you want to always authorize local connections. However, if it is a remote connection, you would like to keep the usual authorization logic.
You could do something like:
public class LocalPermittedAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return (httpContext.Request.IsLocal || base.AuthorizeCore(httpContext)));
}
}
Or you could always authorize a certain remote address (your machine, for example).
That's it!
Edit: forgot to mention, you will use it the same as you would use the AuthorizeAttribute filter:
class MyController : Controller
{
[LocalPermittedAuthorize]
public ActionResult Fire()
{
Missile.Fire(Datetime.Now);
}
}
Yes, take a look at the MSDN docs for AuthorizeAttribute: http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.aspx.
Basically, you can override the OnAuthorization() method and customize the behavior. There are other virtual methods on the attribute as well.
EDIT: As Bruno pointed out, you can override the AuthorizeCore() method. The main difference being that AuthorizeCore() takes an HttpContextBase, while OnAuthorization() takes an AuthorizationContext. An instance of AuthorizationContext provides you with more information, such as the Controller, the RequestContext and the RouteData. It also lets you specify an ActionResult.
AuthorizeCore() is more restricted in the information you can access as well as the result you can return, but if you need to authorize cached data, then your logic needs to handle the case where you don't have any of that extra data (since data is served from the cache before the request is routed through the MVC pipeline).
As always, you need to understand your scenario and the available tools and trade-offs between them.
Implement your own Role Provider and set your app to use it. Then the Authorize attribute will respect your athorization code.
I see only 2 ways: overriding AuthorizeAttribute.OnAuthorization method or creating your own authorize attribute from scratch.
1) very easy:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
/// your behavior here
}
}
2) easy too - just look at ASP.NET MVC source, AuthorizeAttribute.cs file
It seems you can implement a custom filter as usual (and inherit AuthorizeAttribute if you want), and then create a new ActionInvoker that inherits ControllerActionInvoker and overrides GetFilters. In GetFilters, you call base.GetFilters() to get the list of filters, the iterate through the AuthorizationFilters and replace calls to AuthorizeFilter with calls to your custom filter.
Another potential way is to implement custom membership and role providers, depending on what you're trying to do.