DelegatingHandler in ASP.NET Web API

In any real-time application, we call some kind of third-party service or REST API, and any REST API does some kind of request validation like checking token in the request header, etc.. and it's achieved using Filters or Middleware in ASP.NET Core API.

If we think of reverse i.e. validating all the outgoing requests from our application or simply logging the outgoing requests going out of our application then DelegatingHandler in ASP.Net Web API does the same job as middleware does for the incoming requests.

DelegatingHandler

Outbound requests from any web application have many requirements like logging the request or retrying the request for n number of times in case of failure or validating the header or applying a rate limiter to limit the number of requests per minute going to hit external web service etc... so you can create a chain of Delegating handlers that can perform different operations. DelegatingHandler is just the reverse of Middleware for all the outbound requests.
DelegatingHandler is part of the System.Net.Http namespace.

delegating_handler_in_dotnetcore.jpg

Above is a diagram which illustrates that we can have n number of Message Handler and request will from one Message Handler to another one by one. Similarly, the response will flow in a reverse manner.

Creating the Handler


Let's look into a simple message handler that just logs the outgoing request and also logs the response.

public class MessageHandler : DelegatingHandler
{
    private readonly ILogger<MessageHandler> _logger;


    public MessageHandler(ILogger<MessageHandler> logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        _logger.LogInformation($"Logging the Outgoing request", request);
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);


        _logger.LogInformation($"Logging the Outgoing response", request);
        return response;
    }
}


Suppose we want to validate all the outgoing requests whether they are having a header named "api-key" in their request then it can be achieved using the below code.

public class ValidateRequestHandler : DelegatingHandler
{
    private readonly ILogger<ValidateRequestHandler> _logger;


    public ValidateRequestHandler(ILogger<ValidateRequestHandler> logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (!request.Headers.Contains("api-key"))
        {
            return new HttpResponseMessage(HttpStatusCode.BadRequest)
            {
                Content = new StringContent("Please provide an API key header called api-key")
            };
        }
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);


        return response;
    }
}

In the above handler, we can see it returns 400 Bad request if it doesn't find the header named "api-key" in the request.

Add Header to all the outgoing request

public class AddRequestHeaderHandler : DelegatingHandler
{
    private readonly ILogger<AddRequestHeaderHandler> _logger;


    public AddRequestHeaderHandler(ILogger<AddRequestHeaderHandler> logger)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    protected async override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("api-key", GetAPIKey());
        // Call the inner handler.
        var response = await base.SendAsync(request, cancellationToken);


        return response;
    }


    private string GetAPIKey()
    {
        // returning random string for example
        return "jhfsfksnfhsfd;lslhfsdjfslfds";
    }
}

The above method is used for adding a header to all the outgoing requests from the application.

Registering the Handler

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddTransient<ValidateRequestHandler>();


    services.AddRefitClient<IStudentClient>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://localhost:44391"))
            .AddHttpMessageHandler<ValidateRequestHandler>();
}


In the registration of handler, you can see I am using AddRefitHandler which is from Refit Nuget Library and you can check this article to get the knowledge regarding Refit.

Suppose we want to use both MessageHandler as well as ValidateRequestHandler for a particular third-party API then we use like below

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddTransient<ValidateRequestHandler>();
    services.AddTransient<MessageHandler>();


    services.AddRefitClient<IStudentClient>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://localhost:44391"))
            .AddHttpMessageHandler<ValidateRequestHandler>()
            .AddHttpMessageHandler<MessageHandler>();
}


Similarly, if we want to use a different handler for different third party APIs then we can use like below

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddTransient<ValidateRequestHandler>();
    services.AddTransient<MessageHandler>();
    services.AddTransient<AddRequestHeaderHandler>();


    services.AddRefitClient<IStudentClient>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://localhost:44391"))
            .AddHttpMessageHandler<ValidateRequestHandler>()
            .AddHttpMessageHandler<MessageHandler>();


    services.AddRefitClient<IFeesClient>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri("https://localhost:4500"))
            .AddHttpMessageHandler<AddRequestHeaderHandler>();
}


As you can see AddRequestHeaderHandler is only used for IFeesClient third party service and ValidateRequestHandler or MessageHandler is used for IStudentClient third party service.

Conclusion

As we saw DelegatingHandler can help us to perform multiple kinds of tasks for the outgoing request like adding a header, validating the header, logging the request, etc...

Please note if we are calling multiple third-party APIs from our application and we want to use our delegatinghandler for all the third-party APIs then we have to use AddHttpMessageHandler method for all the APIs in the ConfigureServices method.

Hope you understand DelegatingHandler in ASP.Net Core and DelegatingHandler can be used in the Legacy DotNet framework as well and only difference is that of registration.




Share This Post

Linkedin
Fb Share
Twitter Share
Reddit Share

Support Me

Buy Me A Coffee