Inject validator in ASP.NET core filter - asp.net-core

I have the following ASP.NET Core controller action:
public async Task<IActionResult> Post([FromBody]Model model) {
IValidator<Model> validator = new Validator<Model>();
if (!validator.IsValid)
return await validation.ToErrorResponse();
// Remaining code
}
I configured ASP.NET core to inject IValidator on Startup.
I would like to make the validation automatic using a filter:
public class ValidateAttribute : ActionFilterAttribute {
public overrideTask OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next) {
}
}
Inside the filter I need to request the correct Validator according to the Model being submitted and returning the errors.
How can I get the validator and the model being submitted inside the filter?

Related

Can Blazor Use Custom IAuthorizationFilter

Hi I was trying to use TypeFilter and IAuthorizationFilter like discussed in these post:
How do you create a custom AuthorizeAttribute in ASP.NET Core? and How to include multiple policies
for blazor (server side, not blazor wasm, not asp.net core), but the IAuthorizationFilter is never executed.
I want to use IAuthorizationFilter, because using custom policy requirement and IAuthorizationHandler is so not flexible.
I cannot find explicitly that IAuthorizationFilter and Blazor don't work together, every keyword using blazor and custom filter only point to using that policy requirement IAuthorizationRequirement.
So anybody has their blazor server side application works with IAuthorizationFilter? would you mind sharing your resources.
Thank you.
updated:
This is the codes I used from https://stackoverflow.com/a/43788693/423356
public enum PermissionItem
{
User,
Product,
Contact,
Review,
Client
}
public enum PermissionAction
{
Read,
Create,
}
public class AuthorizeAttribute : TypeFilterAttribute
{
public AuthorizeAttribute(PermissionItem item, PermissionAction action)
: base(typeof(AuthorizeActionFilter))
{
Arguments = new object[] { item, action };
}
}
public class AuthorizeActionFilter : IAuthorizationFilter
{
private readonly PermissionItem _item;
private readonly PermissionAction _action;
public AuthorizeActionFilter(PermissionItem item, PermissionAction action)
{
_item = item;
_action = action;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
bool isAuthorized = MumboJumboFunction(context.HttpContext.User, _item, _action); // :)
if (!isAuthorized)
{
context.Result = new ForbidResult();
}
}
}
This is how I declare in my blazor server side .NET 5.0 page:
#attribute [Authorize]
#attribute [Authorize(PermissionItem.User, PermissionAction.Read)]
Using Custom policy requirement works but not flexible, as explained better in my 2 sources above.

How to modify ViewComponent result in ASP.NET Core 3.1

I want to modify the result of ViewComponent by using a filter as we do with MVC ActionFiltersAttribute. I've tried ActionFilterAttribute but it's not working with ViewComponent even it's not calling.
public class BeforeCheckoutCallFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.RouteData.Values["action"].ToString().Equals("ProductDetails_AttributeChange", StringComparison.InvariantCultureIgnoreCase))
{
//Business logic
}
return;
}
}
I'm registering this filter inside Startup.cs
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.Configure<MvcOptions>(options =>
{
options.Filters.Add<BeforeCheckoutCallFilter>();
}
}
Is there a way to get the ViewComponent result and modify it as we were used to doing with MVC filters?
Updated: I want to intercept the call after returning IViewComponentResult.
Note: I've got know that the ViewComponent does not take part in the controller lifecycle, which means we can’t use filters in a view component.
There is no support for direct interception of ViewComponents, as it does not take part of the request pipeline. From the official doc:
A view component class:
Doesn't take part in the controller lifecycle, which means you can't use filters in a view component
But you can do it indirectly by invoking the ViewComponent from an Action instead. Then decorate the Action with your Filter:
[BeforeCheckoutCall]
public IActionResult Checkout()
{
return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

Custom attribute validation in ASP.NET Core is running before JWT token is processed

asp.net core authorization
I am trying to use a custom authorization attribute to have finer control over my controller actions like this (somewhat similar to How do you create a custom AuthorizeAttribute in ASP.NET Core?)
[MyCustomAuth(Permissions="Products/Read")]
public IActionResult SomeMethod()
{
.....
}
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string Permissions { get; set; } //Permission string to get from controller
public void OnAuthorization(AuthorizationFilterContext context)
{
//
//read jwttoken
//and process permissions string
//to decide if user can run controller method
//
..
}
}
Unfortunately the JWT authorization handler that is built into ASP.NET core (configured in startup.cs) is run only AFTER this custom attribute is code is run so I can't seem to access the JWT token and THEN process the custom auth parameters.
Is there anyway to force the JWT token to be processed first and then do an extra validation using the custom attribute?
I think I found a solution...it seems to work..but could someone please confirm this is the right way?
Just implement IOrderedFilter interface and set Order to a high number. This means JWT authentication will be called first and then your custom authorization filter.
public class MyCustomAuthAttribute : AuthorizeAttribute, IAuthorizationFilter
{
...
public int Order => 9999;
...
}

Asp.net core API method is called on fluent api validation failed

I have implemented Fluent API validation with Aspnet Core and MediatR and disabled the default MVC validation.
Previously, On invalid data, the API validation will be called first and then API method will be called.
On invalid data, Fluent API Validation will throw an error and the call won't fired to the api method.
But now, even on invalid data, the api method is called.
what am I missing?
Configuration:
services.AddMvc().AddFluentValidation(fv =>
{
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
});
ValidatorOptions.Global.CascadeMode = CascadeMode.StopOnFirstFailure;
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
Code:
public class LoginCommandValidator : AbstractValidator<LoginCommand>
{
public LoginCommandValidator(IStringLocalizer<Resource> stringLocalizer)
{
this.CascadeMode = CascadeMode.StopOnFirstFailure;
RuleFor(v => v.Username)
.NotEmpty().WithMessage(stringLocalizer["InvalidUsername"])
.NotNull().WithMessage(stringLocalizer["InvalidUsername"]);
RuleFor(v=>v.Password)
.NotEmpty().WithMessage(stringLocalizer["InvalidPassword"])
.NotNull().WithMessage(stringLocalizer["InvalidPassword"]);
}
}
Maybe you can write an ActionFilterAttribute, and then add this filter to your Controller.
Like this:
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreteErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Then add this filter above your controller:
[ValidateModelStateFilter]
Hope this can help you.

IHttpClientFactory using in ActionFilterAttribute [duplicate]

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