How to await method in PropertyValidator's overridden IsValid? - c#

I'm trying to use my Data Repository in PropertyValidator class. The problem is that method in the Repository is async... and I can't see IsValidAsync to override.
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Validators;
using AccountApi.Domain.Repositories;
namespace AccountApi.Domain.Validators
{
public class UserInputValidator<TElement> : PropertyValidator
{
private IUserRepository _userRepository;
public UserInputValidator(IUserRepository userRepository)
: base("{Message}.")
{
_userRepository = userRepository;
}
protected override bool IsValid(PropertyValidatorContext context)
{
var id = (int)context.PropertyValue;
var user = await _userRepository.GetById(id); // <--- HERE is the problem
// .....
return true;
}
}
}
I tried to change to this:
protected override async Task<bool> IsValidAsync(PropertyValidatorContext context)
... but it doesn't work as well. There is no IsValidAsync to override.
Is it possible to use async methods inside PropertyValidator?

You're sort of on the right track.
Yes, you do overload the PropertyValidator method protected virtual async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation)
However you need to use the validator.ValidateAsync(objectToValidate) method to use it.
If your validator contains asynchronous validators or asynchronous
conditions, it’s important that you always call ValidateAsync on your
validator and never Validate. If you call Validate, then your
asynchronous rules will be run synchronously, which is not desirable.
Source
Additionally refer to the following issue where Jeremy suggests:
Also overloading the ShouldValidateAsync method; or better still
Use the AsyncValidatorBase rather than PropertyValidator which handles the ShouldValidateAsync overload for you
See the following if you want to see a working solution of an async property validator.

Is it possible to use async methods inside PropertyValidator?
You could press F12 on PropertyValidator in vs to see its methods.
public abstract class PropertyValidator : IPropertyValidator
{
protected PropertyValidator(IStringSource errorMessageSource);
protected PropertyValidator(string errorMessage);
[Obsolete("This constructor will be removed in FluentValidation 9.0. Use the overload that takes an IStringSource instead, passing in a LazyStringSource: PropertyValidator(new LazyStringSource(ctx => MyResourceClass.MyResourceName))")]
protected PropertyValidator(string errorMessageResourceName, Type errorMessageResourceType);
public PropertyValidatorOptions Options { get; }
public virtual bool ShouldValidateAsync(ValidationContext context);
public virtual IEnumerable<ValidationFailure> Validate(PropertyValidatorContext context);
[AsyncStateMachine(typeof(<ValidateAsync>d__7))]
public virtual Task<IEnumerable<ValidationFailure>> ValidateAsync(PropertyValidatorContext context, CancellationToken cancellation);
//
// Summary:
// Creates an error validation result for this validator.
//
// Parameters:
// context:
// The validator context
//
// Returns:
// Returns an error validation result.
protected virtual ValidationFailure CreateValidationError(PropertyValidatorContext context);
protected abstract bool IsValid(PropertyValidatorContext context);
[AsyncStateMachine(typeof(<IsValidAsync>d__10))]
protected virtual Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation);
//
// Summary:
// Prepares the FluentValidation.Internal.MessageFormatter of context for an upcoming
// FluentValidation.Results.ValidationFailure.
//
// Parameters:
// context:
// The validator context
protected virtual void PrepareMessageFormatterForValidationError(PropertyValidatorContext context);
}
So,try to use
protected override async Task<bool> IsValidAsync(PropertyValidatorContext context, CancellationToken cancellation)

It is simple. Just use "AsyncPropertyValidator" or "AsyncPropertyValidator<T, type>"
In your case it will be like
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentValidation.Validators;
using AccountApi.Domain.Repositories;
namespace AccountApi.Domain.Validators
{
public class UserInputValidator<TElement> : AsyncPropertyValidator
{
private IUserRepository _userRepository;
public UserInputValidator(IUserRepository userRepository)
: base("{Message}.")
{
_userRepository = userRepository;
}
public override async Task<bool> IsValidAsync(PropertyValidatorContext context)
{
var id = (int)context.PropertyValue;
var user = await _userRepository.GetById(id);
// ..
return true;
}
}
}

Related

How to solve having to use an await method in ApiController initialization

So I have a Base controller that all controllers inherit from, and it implements ApiController, in override initialize I have something like this:
protected override void Initialize(HttpControllerContext controllerContext)
{
var tenantId= requestUtils.GetFromHeader(controllerContext.Request);
if (tenantId!= null)
log4net.ThreadContext.Properties["tenantId"] = tenantId;
else
log4net.ThreadContext.Properties["tenantId"] = "default";
await unitOfWork.SetTenantIdentifier(tenantIdentifier);
base.Initialize(controllerContext);
}
I need to set the tenantId for the unitofwork (since I have multitenant solution) and it needs an await before it (since I need to fetch something from the db)but I can't make Initialize an async Task so I'm trying to find a workaround or so.
//await unitOfWork.SetTenantIdentifier(tenantIdentifier);
unitOfWork.SetTenantIdentifier(tenantIdentifier).Wait();
You should verify that there is no risk of deadlock on the Wait().

ActionFilterAttribute: When to use OnActionExecuting vs. OnActionExecutingAsync?

I made a LoggedAttribute class that inherited from System.Web.Http.Filters.ActionFilterAttribute and put logging into OnActionExecuting and OnActionExecutingAsync methods; I had assumed one would be used for async calls and one would be used for non-async calls, but it seems both are being used. So which one should I put my logging in?
UPDATE
Code follows:
public sealed class LoggedAttribute : ActionFilterAttribute
{
private readonly ILog _logger;
public LoggedAttribute(ILogManager logManager)
{
_logger = logManager.GetLogger<LoggedAttribute>();
}
public LoggedAttribute() : this(new LogManager()) {}
public override void OnActionExecuting(HttpActionContext actionContext)
{
//logging logic goes here
base.OnActionExecuting(actionContext);
}
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
//logging logic goes here
await base.OnActionExecutingAsync(actionContext, cancellationToken);
}
}
I then apply the [Logged] attribute to my base ApiController, and I am getting duplicate log entries for single calls.
The reply doesn't answer the question I think,
Both are used if you need to run your code before executing the action.
If you want it also to be ran SYNCHRONOUSLY use OnActionExecuting, otherwise use OnActionExecutingAsync (e.i. Async).
Well, it looks like OnActionExecuting gets called on every single request. I put my logic exclusively in that method, and it seems to work as desired.

Is there a way to create an ActionFilter that wraps the content of the Action in a using statement?

My scenario: My application is a Web Api 2 app using a business logic and repository layer for data access. The web application uses ASP.NET Impersonation to login to the database as the user accessing the website (authenticated via PKI). I have several async controller methods. However, when I await on the data access methods, the database call may complete on a different thread which will then access the database under the identity of my application pool which is not allowed to connect to the database.
Example Controller:
public class TestApiController : ApiController {
private IBusinessLogicObject _myBlObject;
public TestApiController(IBusinessLogicObject myBlObject){
_myBlObject = myBlObject; //Populated through Unity
}
public async Task<int> CountMyJobs(string name){
return await _myBlObject.CountMyJobsAsync(name);
}
}
Example Business Logic Class:
public class BusinessLogicObject : IBusinessLogicObject
{
private IGenericRepository<Job> _jobRepository;
public BusinessLogicObject(IGenericRepository<Job> _jobRepository)
{
_jobRepository = jobRepository; //Populated with Unity
}
public Task<int> CountMyJobsAsync(string name)
{
using (WindowsIdentity.GetCurrent().Impersonate())
{
//JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
return _jobRepository.Where(i => i.Name == name).CountAsync();
}
}
}
If I move the using statement into the controller (wrapped around the await), it works fine.
The issue seems to be that because the await is outside of the impersonation context, it does not impersonate the database call (the CountAsync()) and I am unable to open a connection to my database.
The Question:
Is there a way I could write an ActionFilter or some other attribute on my controller method so that the method itself (containing the await call) would be automatically wrapped in the using statement?
merpmerp's answer is going to be problematic in multi-threaded servers. Since there is only one instance of an ActionFilterAttribute per decorated method, two simultaneous requests to the same method will result in usingVariable being overwritten, and only one will end up being disposed.
You'll need to take this a step further and store the ImpersonationContext somewhere in the request context-- e.g. in filterContext.Request.Properties.
I don't believe there is a way to actually wrap a method in a using statement with an attribute, but you could essentially do the same thing by using the OnActionExecuting and OnResultExecuted methods in a custom ActionFilter.
public class IdentityImpersonateActionFilter : ActionFilterAttribute
{
IDisposable usingVaribale;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
usingVaribale = WindowsIdentity.GetCurrent().Impersonate();
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
usingVaribale.Dispose();
}
}
Then you could decorate your methods or your whole controller class with [IdentityImpersonate]
[IdentityImpersonate]
public Task<int> CountMyJobsAsync(string name)
{
//JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
return _jobRepository.Where(i => i.Name == name).CountAsync();
}
You could also access this using variable in your function if you wish
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
usingVaribale = WindowsIdentity.GetCurrent().Impersonate();
filterContext.ActionParameters.Add("parameterName", usingVaribale);
}
And add the parameter to your controller function
[IdentityImpersonate]
public Task<int> CountMyJobsAsync(object parameterName, string name)
{
//JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
return _jobRepository.Where(i => i.Name == name).CountAsync();
}
Hope this helps!
If you want to keep the impersonation the responsibility of the business logic, then you can just do this:
public async Task<int> CountMyJobsAsync(string name)
{
using (WindowsIdentity.GetCurrent().Impersonate())
{
return await _jobRepository.Where(i => i.Name == name).CountAsync()
.ConfigureAwait(false);
}
}

Add custom header to all responses in Web API

Simple question, and I am sure it has a simple answer but I can't find it.
I am using WebAPI and I would like to send back a custom header to all responses (server date/time requested by a dev for syncing purposes).
I am currently struggling to find a clear example of how, in one place (via the global.asax or another central location) I can get a custom header to appear for all responses.
Answer accepted, here is my filter (pretty much the same) and the line i added to the Register function of the WebApi config.
NOTE: The DateTime stuff is NodaTime, no real reason just was interested in looking at it.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Content.Headers.Add("ServerTime", Instant.FromDateTimeUtc(DateTime.Now.ToUniversalTime()).ToString());
}
Config Line:
config.Filters.Add(new ServerTimeHeaderFilter());
For that you can use a custom ActionFilter (System.Web.Http.Filters)
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
actionExecutedContext.Response.Headers.Add("customHeader", "custom value date time");
}
}
You can then apply the filter to all your controller's actions by adding this in the configuration in Global.asax for example :
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderFilter());
You can also apply the filter attribute to the action that you want without the global cofiguration line.
Previous answers to this question don't address what to do if your controller action throws an exception. There are two basic ways to get that to work:
Add an exception filter:
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class HeaderAdderExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
context.Response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
context.Response.Content.Headers.Add("header", "value");
}
}
and in your WebApi setup:
configuration.Filters.Add(new HeaderAdderExceptionFilter());
This approach works because WebApi's default exception handler will send the HttpResponseMessage created in a filter instead of building its own.
Replace the default exception handler:
using System.Net;
using System.Net.Http;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Results;
public class HeaderAdderExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
HttpResponseMessage response = context.Request.CreateErrorResponse(
HttpStatusCode.InternalServerError, context.Exception);
response.Headers.Add("header", "value");
context.Result = new ResponseMessageResult(response);
}
}
and in your WebApi setup:
configuration.Services.Replace(typeof(IExceptionHandler), new HeaderAdderExceptionHandler());
You can't use both of these together. Okay, well, you can, but the handler will never do anything because the filter already converted the exception into a response.
Super important to note that as written, this code will send all the exception details to the client. You probably don't want to do this in production, so check out all the available overloads on CreateErrorResponse() and pick which one suits your needs.
Julian's answer led me to have to create the filter but only using the the System.Web (v4) and System.Web.Http (v5) namespace (MVC packages were not part of this particular project this was used on.)
using System.Web;
using System.Web.Http.Filters;
...
public class AddCustomHeaderActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
actionExecutedContext.ActionContext.Response.Headers.Add("name", "value");
}
}
And add it to the global.asax to have it used on every controller/action
GlobalConfiguration.Configuration.Filters.Add(new AddCustomHeaderActionFilterAttribute());
Neither of the above two solutions worked for me. They wouldn't even compile. Here's what I did. Added:
filters.Add(new AddCustomHeaderFilter());
to RegisterGlobalFilters(GlobalFilterCollection filters) method in FiltersConfig.cs and then added
public class AddCustomHeaderFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
actionExecutedContext.HttpContext.Response.Headers.Add("ServerTime", DateTime.Now.ToString());
}
}
It can be done by the messagehandler easily, it will handle both ok response and exception case.
public class CustomHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// add header to request if you want
var response = await base.SendAsync(request, cancellationToken);
response.Headers.Add("cutomKey", "cutomValue");
return response;
}
}
Add it in the config
config.MessageHandlers.Add(new CustomHeaderHandler());
According to my requirement, below single line of code serves the purpose.
System.Web.HttpContext.Current.Response.Headers.Add("Key", "Value")
I combined the normal and exception path in one class:
public class CustomHeaderAttribute : FilterAttribute, IActionFilter, IExceptionFilter
{
private static string HEADER_KEY { get { return "X-CustomHeader"; } }
private static string HEADER_VALUE { get { return "Custom header value"; } }
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return (new CustomHeaderAction() as IActionFilter).ExecuteActionFilterAsync(actionContext, cancellationToken, continuation);
}
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
return (new CustomHeaderException() as IExceptionFilter).ExecuteExceptionFilterAsync(actionExecutedContext, cancellationToken);
}
private class CustomHeaderAction: ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response != null)
{
actionExecutedContext.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
private class CustomHeaderException : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
context.Response = context.Request.CreateErrorResponse(HttpStatusCode.InternalServerError, context.Exception);
}
context.Response.Content.Headers.Add(HEADER_KEY, HEADER_VALUE);
}
}
}
Nothing fancy but at least it gives me one place to control my additional headers. For now it's just static content but you could always hook it up to some sort of dictionary generator/factory.
I had the same problem while trying to add a new header to the whole controller, just add "services.AddHttpContextAccessor();" to startup.cs then create your controller
public class EnController : Controller{
public EnController(IHttpContextAccessor myHttpAccessor)
{
myHttpAccessor.HttpContext.Response.Headers.Add("Content-Language", "en-US");
}
... more methods here...
}

Custom filter attributes inject dependency

I'musing ASP.NET Web API and I need to have authorization so I've created custom authorization attribute
public class CustomAuthorizationAttribute : AuthorizeAttribute
In order to inject dependency inside constructor I have following :
public CustomAuthorizationAttribute(IAccountBL accountBl)
{
_accountBL = accountBl;
}
In IAccountBL I have method which interacts with database checking if user is authorized to make request.
Inside Member API controller I've register that attribute
[CustomAuthorization]
public class MemberController : ApiController
But I get following error
Project.Account.AccountBL' does not contain a constructor that takes 0 arguments
And if I register it like
[CustomAuthorization(IAccountBL)]
Thank you
Action filters are just attributes. You do not have control over when those attributes are instantiated by the CLR. One possibility is to write a marker attribute:
public class CustomAuthorizationAttribute : Attribute { }
and then the actual action filter:
public class CustomAuthorizationFilter : ActionFilterAttribute
{
private readonly IAccountBL accountBL;
public CustomAuthorizationFilter(IAccountBL accountBL)
{
this.accountBL = accountBL;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<CustomAuthorizationAttribute>().Any() ||
actionContext.ActionDescriptor.GetCustomAttributes<CustomAuthorizationAttribute>().Any())
{
// here you know that the controller or action is decorated
// with the marker attribute so that you could put your code
}
}
}
and finally register it as a global action filter:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
IAccountBL accountBL = ...
config.Filters.Add(new CustomAuthorizationFilter(accountBL));
}
}
and finally you could use the marker attribute:
[CustomAuthorization]
public class MemberController : ApiController
{
...
}
You can get dependency in your filter by using extension method GetDependencyScope for class HttpRequestMessage. It's not a canonical way for dependency injection, but can be used as workaround. A basic example may look like this:
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var dependencyScope = context.Request.GetDependencyScope();
var dependency = dependencyScope.GetService(typeof (MyDependencyType));
//use your dependency here
}
This method may be used with constructor injection to simplify unit testing:
public class MyAuthenticationFilter : Attribute, IAuthenticationFilter
{
private Func<HttpRequestMessage, MyDependencyType> _dependencyFactory;
public MyAuthenticationFilter() :
this(request => (MyDependencyType)request.GetDependencyScope().GetService(typeof(MyDependencyType)))
{
}
public MyAuthenticationFilter(Func<HttpRequestMessage, MyDependencyType> dependencyFactory)
{
_dependencyFactory = dependencyFactory;
}
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var dependencyScope = context.Request.GetDependencyScope();
var dependency = dependencyFactory.Invoke(context.Request);
//use your dependency here
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public bool AllowMultiple { get; private set; }
}
If anyone finds similar issue here's how I manage to solve it.
My custom filter inherits IAutofacAuthorizationFilter. Besides this one you can also inherit IAutofacExceptionFilter and IAutofacActionFilter.
And inside my DI container I've register this filter for each controller I want to use like this
builder.Register(c => new CustomAuthorizationAttribute(c.Resolve<IAccountBL>()))
.AsWebApiAuthorizationFilterFor<MemberController>()
.InstancePerApiRequest();
If you registered your service on the application using any container, it's very easy to get the instance of your service from anywhere in the scope. Just follow the below code to get your service.
var myService = DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService;
Please make sure you have included System.Web.Mvc in the file.
Happy coding!!!

Categories

Resources