I want to use a DbContext in my ActionFilter Middleware. Is it possible?
public class VerifyProfile : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
using (var context = new SamuraiDbContext())
{
var user = filterContext.HttpContext.User.Identity.Name;
if (context.Profiles.SingleOrDefaultAsync(p => p.IdentityName == user).Result == null)
{
filterContext.Result = new RedirectResult("~/admin/setup");
}
}
}
}
But this code using (var context = new SamuraiDbContext()) requires to pass options. Should I again pass DbContextOptionsBuilder() here or is there any other way?
I want to have [VerifyProfile] attribute in my controllers method. Is it possible or not?
Instead of trying to create a new instance of SamuraiDbContext yourself, use Dependency Injection in your Filter. To achieve this, you need to do three things:
Add a constructor to VerifyProfile with a parameter of type SamuraiDbContext and store it as a field:
private readonly SamuraiDbContext dbContext;
public VerifyProfile(SamuraiDbContext dbContext)
{
this.dbContext = dbContext;
}
Add VerifyProfile to the DI container:
services.AddScoped<VerifyProfile>();
Use ServiceFilter to take care of connecting your filter to the DI container:
[ServiceFilter(typeof(VerifyProfile))]
public IActionResult YourAction()
...
You can apply the ServiceFilter attribute at the action level, as shown, or at the controller level. You can also apply it globally. To do that, replace step 3 above with the following:
services.AddMvc(options =>
{
options.Filters.Add<VerifyProfile>();
});
As an additional resource, this blog post has a good write up of some of the other options.
Related
I have an WebApi application that uses Simple Injector and I'm trying to configure a particular filter with controller attribute (with parameters). I have this configuration working in another project that uses Ninject, but I don't know how to do this on Simple Injector.
public enum UserType {
Director,
Developer,
Leader
}
My controller:
[RequiresAtLeastOneOfUserTypes(UserType.Developer, UserType.Leader)]
public class MyController : Controller
{
...
}
My Attribute:
public sealed class RequiresAtLeastOneOfUserTypesAttribute : Attribute
{
public UserType[] TypesToBeVerified { get; set; }
public RequiresAtLeastOneOfUserTypesAttribute(params UserType[] typesToBeVerified)
{
TypesToBeVerified = typesToBeVerified;
}
}
My Filter:
public class RequiresAtLeastOneOfUserTypesFilter : IActionFilter
{
private readonly IUser _user;
private readonly UserType[] _typesToBeVerified;
protected RequiresAtLeastOneOfUserTypesFilter(IUser user, params UserType[] typesToBeVerified)
{
_user = user;
_typesToBeVerified = typesToBeVerified;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
bool authorized = _user.HasAtLeastOneOfTypes(_typesToBeVerified);
if (!authorized)
{
throw new ForbiddenUserException();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// do nothing
}
}
And finally my Ninject configuration:
this.BindFilter<RequiresAtLeastOneOfUserTypesFilter>(FilterScope.Controller, 0)
.WhenControllerHas<RequiresAtLeastOneOfUserTypesAttribute>()
.WithConstructorArgumentFromControllerAttribute<RequiresAtLeastOneOfUserTypesAttribute>(
"typesToBeVerified",
attribute => attribute.typesToBeVerified);
My question is: How can I do this configuration using Simple Injector?
The Simple Injector Web API integration packages don't contain an integration feature for action filters as Ninject's integration package does. But such integration can be built in a few lines of code.
There are a few options here. The first option is to revert to resolving services directly from inside your action filter, as demonstrated inside the documentation. This approach is fine when you have a single filter class, but isn't the cleanest approach, and would force you to make changes to your already created filter attribute.
As a second option you can, therefore, create a action filter proxy class, that is able to forward the call to your real filter class, which can than be resolved by Simple Injector:
public class ActionFilterProxy<T> : IActionFilter
where T : IActionFilter
{
public ActionFilterProxy(Container container) => _container = container;
public void OnActionExecuting(ActionExecutingContext filterContext) =>
_container.GetInstance<T>().OnActionExecuting(filterContext);
public void OnActionExecuted(ActionExecutedContext filterContext) =>
_container.GetInstance<T>().OnActionExecuted(filterContext);
}
Using this proxy, you can make the following configuration:
GlobalConfiguration.Configuration.Filters.Add(
new ActionFilterProxy<RequiresAtLeastOneOfUserTypesFilter>(container));
container.Register<RequiresAtLeastOneOfUserTypesFilter>();
This still forces you to make a change to RequiresAtLeastOneOfUserTypesFilter, because Simple Injector can't provide the attribute's information (the UserType[]) to RequiresAtLeastOneOfUserTypesFilter's constructor. Instead,you can change RequiresAtLeastOneOfUserTypesFilter to the following:
public class RequiresAtLeastOneOfUserTypesFilter : IActionFilter
{
private readonly IUser _user;
public RequiresAtLeastOneOfUserTypesFilter(IUser user) => _user = user;
public void OnActionExecuting(ActionExecutingContext filterContext)
{
// Get the attribute from the controller here
var attribute = filterContext.ActionDescriptor.ControllerDescriptor
.GetCustomAttribute<RequiresAtLeastOneOfUserTypesAttribute>();
bool authorized = _user.HasAtLeastOneOfTypes(attribute.TypesToBeVerified);
if (!authorized)
{
throw new ForbiddenUserException();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
A third option to use is the one referred to in the documentation, which is described in this blog post, which discusses a model where you place your filters behind an application-specific abstraction and allow them to be Auto-Registered. It uses the a similar proxy approach. This method is useful when you have multiple/many filters that need to be applied (where their order of execution is irrelevant).
I have a base controller and before every page load I want to get the current user. I originally had a constructor in my BaseController that looked like this
public BaseController(ISystemUserCommand command)
{
_systemUserCommand = command
}
The problem with this then is that every controller that inherits from the BaseController would have to contain the ISystemUserCommand in its constructor, which I don't think would be good.
Instead I tried to create just an instance of the service class (shown below - it's the commented line under var sid...) but I need to pass in user service. How would I pass in the user service here or is this a bad way of doing it?
public abstract class BaseController : Controller
{
public SystemUserViewModel CurrentUser { get; set; }
private readonly ISystemUserCommand _systemUserCommand;
public SystemUserViewModel GetCurrentUser()
{
if (HttpContext == null || HttpContext.User == null) return null;
if (CurrentUser != null) return CurrentUser;
var sid = System.Web.HttpContext.Current.Request.LogonUserIdentity.User.ToString();
//var command = new SystemUserCommand();
CurrentUser = _systemUserCommand.GetUser(sid);
return CurrentUser;
}
public void SetUserInformation(SystemUserViewModel currentUser)
{
ViewBag.UserId = currentUser.SystemUserId;
ViewBag.FullName = string.Format("{0} {1}", currentUser.FirstName, currentUser.LastName);
ViewBag.FirstName = currentUser.FirstName;
ViewBag.LastName = currentUser.LastName;
ViewBag.CurrentUser = currentUser;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentUser = GetCurrentUser();
if (currentUser != null)
{
if (currentUser.IsActive)
{
SetUserInformation(currentUser);
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
base.OnActionExecuting(filterContext);
}
}
public class SystemUserCommand : ISystemUserCommand
{
private readonly ISystemUserBusiness _systemUserBusiness;
public SystemUserCommand(ISystemUserBusiness systemUserBusiness)
{
_systemUserBusiness = systemUserBusiness;
}
...
}
You could use property injection instead of constructor injection, via the base class, eg using unity:
public abstract class BaseController : Controller
{
[Dependency]
public ISystemUserCommand SystemUserCommand { get; set; }
}
This would mean the interface reference is only on the base class.
See here for the full examples.
EDIT, Autofac example:
You don't need property attributes on the dependency,
public abstract class BaseController : Controller
{
public ISystemUserCommand SystemUserCommand { get; set; }
}
Just to register the properites to auto resolve on the autofac builder:
builder.RegisterControllers(typeof(MvcApplication).Assembly).Where(t => t.IsAssignableFrom(typeof(BaseController))).PropertiesAutowired();
See autofac property injection here.
First of all, it does not seem a good idea to have OnActionExecuting override in the controller. You can use filters, that are specially designed for this purpose. And it seems that is the main reason you created the BaseController at all.
Regarding the problem with injecting the system command in all the required service, I would do so, but without inheriting from a base class, since I generally prefer aggregation to inheritance. That would mean that each controller that needs to work with the service will get it.
Another option that I have used few times to abstract some operations is to create a UserSerivce that will provide the required operations to the controllers. It will have ISystemUserCommand and HttpContext injected inside so that all of your controllers won't have to do the job. You can either use HttpContext.Current as static or abstract it away if you need testability.
Moreover I would not recommend property injection since it is more obscure than constructor injection that should be preferred if possible.
You can read more about filters here. Unfortunately if you use filters it's not that easy to inject in filters themselves and mostly done with property injection or ServiceLocator pattern (which is not good usually). It's possible to do better with some amount of voodoo though. I think that SimpleInjector has a lot of examples and tutorials on how to apply DI to filters in MVC, maybe they even have a nuget package now to ahieve that.
Every call to my WebAPI may (or may not) contain the GET parameter
/api/SomeControllerFunction?loglevel=(someint)
From the function inside the controller I can initialize a LogCollector:
[HttpGet]
SomeControllerFunction(int loglevel = 0)
{
LogCollector logger = new LogCollector(loglevel)
}
To not repeat myself too often, I want to hide this in the class hierarchy by adding it into the constructor of a BaseController, from which all my controllers shall inherit:
public class BaseController: ApiController
{
internal LogCollector Logger
BaseController()
{
Logger = new LogCollector(loglevel);
}
But how can I access a GET parameter from the constructor?
Instead of using the constructor you could inject the LogCollector directly into the method. If you did want to use the constructor you should use a Di / IoC framework as that would be more appropriate.
In the example below you can use a custom ActionFilterAttribute instance which injects the Logger based the incoming (optional) log level. The log level is then defined in the route using a RouteAttribute on the action. The RouteAttribute also defines a default value for the log level so it is not required when calling that action.
LogInjectorFilterAttribute.cs
public class LogInjectorFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
const string key = "loglevel";
if(actionContext.ControllerContext.RouteData.Values.ContainsKey(key))
{
var loglevel = int.Parse(actionContext.ControllerContext.RouteData.Values[key].ToString());
LogCollector logger = new LogCollector(loglevel);
actionContext.ActionArguments["logger"] = logger;
}
base.OnActionExecuting(actionContext);
}
}
HomeController.cs
[HttpGet]
[Route("api/Home/Get/{loglevel:int=1}")]
[LogInjectorFilter]
public IHttpActionResult Get(LogCollector logger)
{
}
The constructor is invoked too early, you can't access the parameters from there. However, you can override the Initialize method and retrieve the GET parameters from the context:
protected override void Initialize(HttpControllerContext controllerContext)
{
foreach (var parameter in controllerContext.Request.GetQueryNameValuePairs())
{
Debug.WriteLine(string.Format("{0} = {1}", parameter.Key, parameter.Value));
}
base.Initialize(controllerContext);
}
I am trying to inject a service into my action filter but I am not getting the required service injected in the constructor. Here is what I have:
public class EnsureUserLoggedIn : ActionFilterAttribute
{
private readonly ISessionService _sessionService;
public EnsureUserLoggedIn()
{
// I was unable able to remove the default ctor
// because of compilation error while using the
// attribute in my controller
}
public EnsureUserLoggedIn(ISessionService sessionService)
{
_sessionService = sessionService;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Problem: _sessionService is null here
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
}
And I am decorating my controller like so:
[Route("api/issues"), EnsureUserLoggedIn]
public class IssueController : Controller
{
}
Startup.cs
services.AddScoped<ISessionService, SessionService>();
Using these articles as reference:
ASP.NET Core Action Filters
Action filters, service filters and type filters in ASP.NET 5 and MVC 6
Using the filter as a ServiceFilter
Because the filter will be used as a ServiceType, it needs to be registered with the framework IoC. If the action filters were used directly, this would not be required.
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddMvc();
services.AddScoped<ISessionService, SessionService>();
services.AddScoped<EnsureUserLoggedIn>();
...
}
Custom filters are added to the MVC controller method and the controller class using the ServiceFilter attribute like so:
[ServiceFilter(typeof(EnsureUserLoggedIn))]
[Route("api/issues")]
public class IssueController : Controller {
// GET: api/issues
[HttpGet]
[ServiceFilter(typeof(EnsureUserLoggedIn))]
public IEnumerable<string> Get(){...}
}
There were other examples of
Using the filter as a global filter
Using the filter with base controllers
Using the filter with an order
Take a look, give them a try and see if that resolves your issue.
Hope this helps.
Global filters
You need to implement IFilterFactory:
public class AuthorizationFilterFactory : IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
// manually find and inject necessary dependencies.
var context = (IMyContext)serviceProvider.GetService(typeof(IMyContext));
return new AuthorizationFilter(context);
}
}
In Startup class instead of registering an actual filter you register your filter factory:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilterFactory());
});
One more way for resolving this problem. You can get your service via Context as in the following code:
public override void OnActionExecuting(ActionExecutingContext context)
{
_sessionService = context.HttpContext.RequestServices.GetService<ISessionService>();
if (_sessionService.LoggedInUser == null)
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Result = new JsonResult("Unauthorized");
}
}
Please note that you have to register this service in Startup.cs
services.AddTransient<ISessionService, SessionService>();
Example
private ILoginService _loginService;
public override void OnActionExecuting(ActionExecutingContext context)
{
_loginService = (ILoginService)context.HttpContext.RequestServices.GetService(typeof(ILoginService));
}
Hope it helps.
After reading this article ASP.NET Core - Real-World ASP.NET Core MVC Filters (Aug 2016) I implemented it like this:
In Starup.cs / ConfigureServices:
services.AddScoped<MyService>();
In MyFilterAttribute.cs:
public class MyFilterAttribute : TypeFilterAttribute
{
public MyFilterAttribute() : base(typeof (MyFilterAttributeImpl))
{
}
private class MyFilterAttributeImpl : IActionFilter
{
private readonly MyService _sv;
public MyFilterAttributeImpl(MyService sv)
{
_sv = sv;
}
public void OnActionExecuting(ActionExecutingContext context)
{
_sv.MyServiceMethod1();
}
public void OnActionExecuted(ActionExecutedContext context)
{
_sv.MyServiceMethod2();
}
}
}
In MyFooController.cs :
[MyFilter]
public IActionResult MyAction()
{
}
Edit: Passing arguments like [MyFilter("Something")] can be done using the Arguments property of the TypeFilterAttribute class: How do I add a parameter to an action filter in asp.net? (rboe's code also shows how to inject things (the same way))
While the question implicitly refers to "filters via attributes", it is still worth highlighting that adding filters "globally by type" supports DI out-of-the-box:
[For global filters added by type] any constructor dependencies will be populated by dependency injection (DI). Adding a filter by type is equivalent to filters.Add(new TypeFilterAttribute(typeof(MyFilter))).
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
With regards to attribute-based filters:
Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by dependency injection (DI). This is because attributes must have their constructor parameters supplied where they're applied. This is a limitation of how attributes work.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.2#dependency-injection
However, as mentioned in the previous answers to the OP, there are ways of indirection that can be used to achieve DI. For the sake of completeness, here are the links to the official docs:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute
In an MVC project I'm creating I have the following RequirePermissionAttribute that gets put on any action that needs specific permissions (it's been simplified for this example):
public class RequirePermissionAttribute : ActionFilterAttribute, IAuthorizationFilter
{
public Operation Permissions { get; set; }
public RequirePermissionAttribute() { }
public RequirePermissionAttribute(Operation permissions)
{
this.Permissions = permissions;
}
public bool AuthorizeCore(HttpContextBase httpContext)
{
IAuthorizationService authServ = new ASPNETAuthorizationService();
return authServ.Authorize(httpContext);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
Enforce.ArgNotNull(filterContext);
if (this.AuthorizeCore(filterContext.HttpContext))
{
// code snipped.
}
else
{
// code snipped.
}
}
}
So the problem obviously with this is that my authorize attribute has a dependency on the ASPNETAuthorizationService that I created. I can't go the constructor way since attributes are compile-time checked.
One thing to mention, I'm using my own little IoC that I made and it doesn't have support for property injection (yet). Of course, if I did go the property injection route, I'd have to add support for it (which I'd have to do some research on).
What's the best way to inject something into an attribute class?
What's the best way to inject something into an attribute class?
Strictly speaking, we cannot use dependency injection to inject a dependency into an attribute. Attributes are for metadata not behavior. [AttributeSpecification()] encourages this by forbidding reference types as arguments.
What you're probably looking for is to use an attribute and a filter together, and then to inject dependencies into the filter. The attribute adds metadata, which determines whether to apply the filter, and the filter receives the injected dependencies.
How to use dependency injection with an attribute?
There are very few reasons to do this.
That said, if you're intent on injecting into an attribute, you can use the ASP.NET Core MVC IApplicationModelProvider. The framework passes dependencies into the provider's constructor, and the provider can pass dependencies to the attribute's properties or methods.
In your Startup, register your provider.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.TryAddEnumerable(ServiceDescriptor.Transient
<IApplicationModelProvider, MyApplicationModelProvider>());
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
Use constructor injection in the provider, and pass those dependencies to the attribute.
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Routing;
public class MyApplicationModelProvider : IApplicationModelProvider
{
private IUrlHelperFactory _urlHelperFactory;
// constructor injection
public MyApplicationModelProvider(IUrlHelperFactory urlHelperFactory)
{
_urlHelperFactory = urlHelperFactory;
}
public int Order { get { return -1000 + 10; } }
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
foreach (var controllerModel in context.Result.Controllers)
{
// pass the depencency to controller attibutes
controllerModel.Attributes
.OfType<MyAttribute>().ToList()
.ForEach(a => a.UrlHelperFactory = _urlHelperFactory);
// pass the dependency to action attributes
controllerModel.Actions.SelectMany(a => a.Attributes)
.OfType<MyAttribute>().ToList()
.ForEach(a => a.UrlHelperFactory = _urlHelperFactory);
}
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
// intentionally empty
}
}
Create an attribute with public setters that can receive dependencies.
using System;
using Microsoft.AspNetCore.Mvc.Routing;
public sealed class MyAttribute : Attribute
{
private string _someParameter;
public IUrlHelperFactory UrlHelperFactory { get; set; }
public MyAttribute(string someParameter)
{
_someParameter = someParameter;
}
}
Apply the attribute to a controller or an action.
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[MyAttribute("SomeArgument")]
public class ValuesController : Controller
{
[HttpGet]
[MyAttribute("AnotherArgument")]
public string Get()
{
return "Foobar";
}
}
The above demonstrates one way, for the rare use case, that you can inject dependencies into an attribute. If you figure out a valid reason to do this, please post it in the comments.
I originally thought this was not possible, but I stand corrected. Here's an example with Ninject:
http://codeclimber.net.nz/archive/2009/02/10/how-to-use-ninject-to-inject-dependencies-into-asp.net-mvc.aspx
Update 2016-10-13
This is a pretty old question by now, and frameworks have changed quite a bit. Ninject now allows you to add bindings to specific filters based on the presence of specific attributes, with code like this:
// LogFilter is applied to controllers that have the LogAttribute
this.BindFilter<LogFilter>(FilterScope.Controller, 0)
.WhenControllerHas<LogAttribute>()
.WithConstructorArgument("logLevel", Level.Info);
// LogFilter is applied to actions that have the LogAttribute
this.BindFilter<LogFilter>(FilterScope.Action, 0)
.WhenActionHas<LogAttribute>()
.WithConstructorArgument("logLevel", Level.Info);
// LogFilter is applied to all actions of the HomeController
this.BindFilter<LogFilter>(FilterScope.Action, 0)
.WhenControllerTypeIs<HomeController>()
.WithConstructorArgument("logLevel", Level.Info);
// LogFilter is applied to all Index actions
this.BindFilter(FilterScope.Action, 0)
.When((controllerContext, actionDescriptor) =>
actionDescriptor.ActionName == "Index")
.WithConstructorArgument("logLevel", Level.Info);
This is in keeping with the principle, argued by Mark Seeman and by the author of Simple Injector, which is that you should keep the logic of your action filter separate from the custom attribute class.
MVC 5 and 6 also make it far easier to inject values into attributes than it used to be. Still, separating your action filter from your attribute is really the best approach to take.