Image by awcreativeut
MVC Filters are discussed in detail at docs.microsoft.com
Filters in ASP.NET Core allow code to run before or after specific stages in the request processing pipeline.
Use case of TypeFilterAttribute?
Suppose you are writing a Filter Attribute of IAsyncResultFilter
which gets executed when the action method completes unless the request response is short-circuited.
public sealed class RemoveCacheResultFilter : Attribute, IAsyncResultFilter
{
private readonly IDistributedCache _distributedCache;
public RemoveCacheResultFilter(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
await _distributedCache.RemoveAsync("x").ConfigureAwait(false);
if (!context.Cancel) await next().ConfigureAwait(false);
}
}
It can be applied to the controller or its action method's, but it has dependency on IDistrubutedCache
which can not be specified.
So to fix this problem Microsoft.AspNetCore.Mvc
has special filter type TypeFilterAttribute
which creates the underlying filter instance and resolves any dependencies. It can be achieved using the below two approaches:
[ApiController]
[Route("[controller]")]
public class UsersController : Controller
{
private readonly ILogger<UsersController> _usersLogger;
public UsersController(ILogger<UsersController> usersLogger)
{
_usersLogger = usersLogger;
}
[HttpGet(Name = "users/{userId:int}")]
[TypeFilter(typeof(RemovePlayerInstanceCacheResultFilter))]
[Produces(typeof(UserApiModel))]
public async Task<IResult> Get(int userId, CancellationToken cancellationToken)
{
if (userId <= 0) return Results.BadRequest();
_usersLogger.LogInformation("Sending query to the database for the user with Id {UserId}", userId);
//code omittted
return user is { } ? Results.Ok(user) : Results.NotFound();
}
internal record UserApiModel(int Id, [UsedImplicitly] string FirstName, string LastName);
}
public class RemoveCacheFilterAttribute : TypeFilterAttribute
{
public RemoveCacheFilterAttribute() : base(
typeof(RemovePlayerInstanceCacheResultFilter))
{
}
// ReSharper disable once MemberCanBePrivate.Global
public sealed class RemoveCacheResultFilter : IAsyncResultFilter
{
private readonly IDistributedCache _distributedCache;
public RemoveCacheResultFilter(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
await _distributedCache.RemoveAsync("x").ConfigureAwait(false);
if (!context.Cancel) await next().ConfigureAwait(false);
}
}
}
public class RemoveCacheFilterAttribute : TypeFilterAttribute
{
public RemoveCacheFilterAttribute() : base(
typeof(RemovePlayerInstanceCacheResultFilter))
{
}
// ReSharper disable once MemberCanBePrivate.Global
public sealed class RemoveCacheResultFilter : IAsyncResultFilter
{
private readonly IDistributedCache _distributedCache;
public RemoveCacheResultFilter(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
await _distributedCache.RemoveAsync("x").ConfigureAwait(false);
if (!context.Cancel) await next().ConfigureAwait(false);
}
}
}
Feedback
I would love to hear your feedback, feel free to share it on Twitter.