Injeção de dependência com atributos na API .NET

· 3 min de leitura

A injeção de dependência é provavelmente um dos melhores recursos que temos no .NET neste momento. Não há nenhuma maneira possível de você não estar usando-o, então se você é como eu, você deseja adicioná-lo a todas as implementações que fizer.

Filtros, conforme [documentação](https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-3.1 oficial da Microsoft):

Os filtros no ASP.NET Core permitem que o código seja executado antes ou depois de estágios específicos no pipeline de processamento de solicitações.

Filtros integrados lidam com tarefas como:

  • Autorização, impedindo o acesso a recursos para os quais um usuário não está autorizado.
  • Cache de resposta, causando curto-circuito no pipeline de solicitação para retornar uma resposta armazenada em cache.

Filtros personalizados podem ser criados para lidar com questões transversais. Exemplos de preocupações transversais incluem tratamento de erros, armazenamento em cache, configuração, autorização e registro. Os filtros evitam a duplicação de código.

Eu trabalho muito com APIs e há algumas coisas que devem executar cada solicitação, ou praticamente todas elas, então, idealmente, o que queremos fazer é trabalhar com isso mais…. injeção de dependência!

Mas às vezes é um pouco complicado, não funciona como queremos se quisermos herdar de ActionAttribute então precisamos trabalhar com TypeFilterAttribute, que nos permite fazer coisas ao substituir OnActionExecutionAsync.

Eu costumo criar esses filtros para fazer alguns registros, então vamos usar isso como exemplo:

/// <summary>
/// LoggedQueryAttribute class
/// </summary>
public class LoggedQueryTypeFilterAttribute : TypeFilterAttribute
{
    /// <summary>
    /// Constructor for <see cref="LoggedQueryTypeFilterAttribute"/>
    /// </summary>
    public LoggedQueryTypeFilterAttribute() : base(typeof(LoggedQueryFilter))
    {
    }

    /// <summary>
    /// LoggedQueryFilter class
    /// </summary>
    private class LoggedQueryFilter : IAsyncActionFilter
    {
        /// <summary>
        /// <see cref="_loggingService"/> object
        /// </summary>
        private readonly LoggingService _loggingService;

        /// <summary>
        /// Constructor for <see cref="LoggedQueryFilter"/>
        /// </summary>
        /// <param cref="LoggingService" name="loggingService">Parameter for loggingService</param>
        public LoggedQueryFilter(LoggingService loggingService)
        {
            _loggingService = loggingService;
        }

        /// <summary>
        /// OnActionExecutionAsync
        /// </summary>
        /// <param cref="ActionExecutingContext" name="context">Parameter for context</param>
        /// <param cref="ActionExecutionDelegate" name="next">Parameter for next</param>
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // Get properties
            var properties = (Request)context.ActionArguments.First().Value!;

            // Get call from context
            var call = context.HttpContext.Request.Path.Value!;

            // Logging
            _loggingService.LogCustomEvent(call);

            // Continue call
            await next();
        }
    }
}

A lógica é bem simples, obtemos o corpo acessando o objeto context com context.ActionArguments.First().Value, também obtemos a chamada do método com context.HttpContext.Request.Path.Value.

Então apenas chamamos nosso método do nosso serviço, neste caso é _loggingService.LogCustomEvent(call).

Então, devemos chamar await next();, porque o pipeline deve continuar.

Isto é para o atributo, agora devemos incluir esse atributo em um método.

[LoggedQueryTypeFilterAttribute]
public ActionResult<string> TestFilter()
{
    return Ok("Hello world!");
}

Espero que tenham gostado, se tiver alguma dúvida ou quiser entrar em contato, não hesite e entre em contato comigo!