Caner Tosuner

Leave your code better than you found it

Asp.Net Core HttpClientFactory Kullanımı

Bu yazıda Asp.Net Core 2.1 ile birlikte gelen HttpClientFactory sınıfını inceleyeceğiz. Asp.Net Core 2.0 sürümünde HttpClient sınıfı ile ilgili ciddi sorunlar vardı. Bunlardan en bariz olanı yük altında çalışan uygulamalarda socket connection mangement tarafında bir takım sorunlar olmasıydı. Herhangi bir .net core 2.0 uygulaması üzerinde HttpClient sınıfını kullanarak bir loop içerisinde htpp call yapıp netstat çektiğinizde açılan socket'lerde TimeWait'ler oluşturduğunu bununda uygulamalarda ciddi sorunlara sebebiyet verdiği gözlemlendi. Github'da açılan issue'lar vs derken Microsoft tarafında geliştirme yapan team sorunları kabul ederek 2.1 versiyonu ile HttpClient'ı yeniden ele alacakalrını belirttiler ve 2.1 release'den sonra hayatımıza HttpClientFactory sınıfı girdi.

HttpClientFactory, doğru memory management'ı yaparak http istekleri yapmamızı sağlan HttpClient(.net 4.5 ile geldi) sınıfının instance'ını oluşturmak için kullanılan sınıftır. HttpClient sınıfının çok fazla instance'ını oluşturmak uygulamalar için maliyetli bir işlemdir. Her yeni bir instance remote server için yeni bir connection demektir. Çok fazla trafiğin olduğu bir uygulamada ise gerektiğinden fazla httpClient instance'ı oluşturmak uygulama için kullanılabilecek socket'lerin tüketilmesi demektir ki bunu istemeyiz.

Bu sınıf HttpClient instance'larının doğru yönetilmesini sağlar ve böylelikle yukarıda bahsettiğimiz sorunları çözdüğünden oldukça önemli bir feature dır.

3 farklı kullanım şekli sunulmuştur;

  • HttpClientFactory doğrudan kullanma
  • Named Client Oluşturma
  • Typed Client Oluşturma

Asp.Net Core 2.1 versiyonu ile birlikte HttpClient kullanımı uygulama servislerinde ayrı bir feature olarak sunulmuştur ve bizimde bu sınıfı kullanabilmek için yapmamız gereken feature'ı uygulamada kullancağımızı belirttiğimiz aşağıdaki kod satırını Startup.cs içerisinde ConfigureServices metoduna yazmak.

services.AddHttpClient();

1) HttpClientFactory Sınıfını Doğrudan Kullanarak

ApiController seviyesinden doğrudan HttpClientFactory sınıfını inject ederek ihtiyaç duyulan yerde httpClient instance'ı yaratabiliriz.

public class FooController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;
  
    public FooController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }
  
    [HttpGet]
    public async Task<ActionResult> GetHomePage()
    {
        var client = _httpClientFactory.CreateClient();
        client.BaseAddress = new Uri("https://www.microsoft.com");
        string result = await client.GetStringAsync("/");
        return Ok(result);
    }
}

2) Named Clients

Bir diğer kullanım şekli ise ilgili domain'e özel named client'lar oluşturmak. Örneğin uygulamada www.microsoft.com  domaininde bulunan adreslere birden fazla request attığınız bir case için bu domaine özel custom client oluşturmak daha performanslı olmakta. Yukarıda kullanıdğımız AddHttpClient HttpClient sınıfını doğrudan kullanmamızı sağladığı için yazmıştık. NamedClient içinse yine bu metodu bu sefer "microsoft" ismine özel bir client tanımlaması olduğundan aşağıdaki gibi yazalım.

services.AddHttpClient("microsoft", c =>
{
    c.BaseAddress = new Uri("https://www.microsoft.com");
    c.DefaultRequestHeaders.Add("CustomHeaderKey", "It-is-a-HttpClientFactory-Sample");
});

Kullanım olarak ise yine apiController içerisinde aşağıdaki gibi "microsoft" ismindeki client'ı factory'den isteyebiliriz.

public class FooController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;
  
    public FooController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }
  
    [HttpGet]
    public async Task<ActionResult> GetHomePage()
    {
        var client = _httpClientFactory.CreateClient("microsoft");
        string result = await client.GetStringAsync("/");
        return Ok(result);
    }
}

3) Typed Clients

 

Üçüncü ve son kullanım şekli ise yine ilgili domain'e özel custom typed client sınıfı oluşturabiliriz.

public class MicrosoftHttpClient
{
    public HttpClient Client { get; private set; }
    
    public MicrosoftHttpClient(HttpClient httpClient)
    {
        httpClient.BaseAddress = new Uri("https://www.microsoft.com/");
        httpClient.DefaultRequestHeaders.Add("CustomHeaderKey", "It-is-a-HttpClientFactory-Sample");
        Client = httpClient;
    }
}

Yukarıda oluşturduğumuz bu custom client sınıfını ConfigureServices metodu içerisinde uygulama servislerine aşağıdaki gibi register ederek ihtiyaç duyduğumuz yerde kullanabiliriz.

services.AddHttpClient<MicrosoftHttpClient>();

Controller içerisinde ise constructor'a MicrosoftHttpClient sınıfını inject etmemiz yeterli.

public class FooController : ControllerBase
{
    private readonly MicrosoftHttpClient _microsoftHttpClient;
  
    public FooController(MicrosoftHttpClient microsoftHttpClient)
    {
        _microsoftHttpClient = microsoftHttpClient;
    }
  
    [HttpGet]
    public async Task<ActionResult> GetHomePage()
    {
        string result = await _microsoftHttpClient.client.GetStringAsync("/");
        return Ok(result);
    }
}

Dilerseniz MicrosoftHttpClient sınıfını IMicrosoftHttClient adında bir interface'den türetip kullanmak istediğiniz yerde constructor sevisyesinde bu interface'i de inject ederek deneyebilirsiniz.

Özellikle trafiğin çok yoğun olduğu uygulamalarda başka bir remote server'a http call yaparak birşeyler consume etmek istediğinizde external connection management'ı oldukça önemli bir hal almakta. Her ne kadar microsoftun geliştirdiği ürünlerde bir çok şeyi biz developer'lara bırakmadan arka planda kendisi halletsede asp.net core 2.0 da acı bir şekilde deneyimlediğimiz gibi yukarıdaki gibi benzer sorunlar olabilmekte. HttpClientFactory sınıfının 3 farklı kullanım şeklini ele aldık ve bunlardan herhangi birini ihtiyacınız doğrultusunda kullanarak remote server call işlemlerinizi kolayca güvenli bir şekilde yapabilirsiniz.