Utilizando HttpClient con IHttpClientFactory en .Net 6

airarrazabald

Abelardo Ruben Irarrázabal Díaz

Posted on October 21, 2022

Utilizando HttpClient con IHttpClientFactory en .Net 6

Para los que están entrando o ya llevamos un tiempo en el mundo de .Net y necesitan consumir una api rest, es muy común que hayan utilizado HttpClient para realizarlo. Hay otras librerías como RestSharp que también permiten consumir servicios, pero RestSharp sigue utilizando HttpClient.

Uno de los grandes problemas que nos podemos encontrar al implementar HttpClient es la mala implementación, volviendo nuestra aplicación inestable, como en el siguiente código:



using(var client = new HttpClient())
{
    //Realizamos solicitudes HTTP
}


Enter fullscreen mode Exit fullscreen mode

En este post no profundizaremos en los errores que pueden ocasionarse con una mala implementación, nos centraremos en como realizar una implementación mas limpia y estable con IHttpClientFactory.

Definición de IHttpClientFactory

La documentación de Microsoft nos define lo siguiente:

Se trata de una interfaz que se usa para configurar y crear HttpClient instancias en una aplicación mediante inserción de dependencias (DI). También proporciona extensiones para el middleware basado en Polly a fin de aprovechar los controladores de delegación en HttpClient.

Lo que quiere decir que a través de esta interfaz generaremos instancias que podremos inyectar en nuestro servicio o clase en donde utilizaremos IHttpClientFactory asegurando una mayor estabilidad y de forma centralizada.

Tambien nos menciona Polly, que en resumen es una librería que nos permite realizar reintentos de request cuando el codigo http de un response coincida con algún codigo que le indiquemos. Es mucho mas sencilla su implementación con IHttpClientFactory, pero la política de reintentos ya es material para otro post.

Implementemos la interfaz

Ahora que ya sabemos el concepto básico de IHttpClientFactory, procedamos a implementarlo en el código

Prerequisitos

  • Visual Studio 2022

1. Creando el proyecto

  1. Lo primero es crear un proyecto con la plantilla de tipo ASP .NET Core Web API

  2. El nombre no es relevante pero para esta ocasión será HttpClientApi

  3. Seleccionamos el framework .NET 6.0 y habilitamos compatibilidad con OpenAPI (swagger)

2. Estructura del proyecto

La estructura del proyecto quedar según la siguiente imagen, creando carpetas por cada capa de nuestra aplicación. Esto es solo para efecto de ejemplo, ya que en vez de carpetas podríamos generar librerías de clases por cada capa.

Estructura del proyecto

3. Implementemos IHttpClientFactory

Ya tenemos lista nuestra estructura !!!
Ahora vamos al código

Clases de la capa Models



namespace HttpClientApi.Models;

public class Bird
{
    public string Uid { get; set; }
    public Name Name { get; set; }
    public Images Images { get; set; }
    public Links _Links { get; set; }
    public int Sort { get; set; }
}


Enter fullscreen mode Exit fullscreen mode


namespace HttpClientApi.Models;

public class Images
{
    public string Main { get; set; }
    public string Full { get; set; }
    public string Thumb { get; set; }
}


Enter fullscreen mode Exit fullscreen mode


namespace HttpClientApi.Models;

public class Links
{
    public string Self { get; set; }
    public string Parent { get; set; }
}



Enter fullscreen mode Exit fullscreen mode


namespace HttpClientApi.Models
{
    public class Name
    {
        public string Spanish { get; set; }
        public string English { get; set; }
        public string Latin { get; set; }
    }
}



Enter fullscreen mode Exit fullscreen mode

Ahora  debemos modificar el appsettings.json para dejar la url que consumiremos con nuestro HttpClient:

appsettings.json:



{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Endpoint": {
    "UrlBirds": "https://aves.ninjas.cl/api/birds"
  }
}


Enter fullscreen mode Exit fullscreen mode
  • Agregamos la propiedad Endpoint:UrlBirds donde se encuentra la url.

Para implementar IHttpClientFactory modificaremos el archivo Program.cs

Program.cs:



using HttpClientApi.Services;

var builder = WebApplication.CreateBuilder(args);

var configuration = builder.Configuration;
builder.Services.AddControllers();

builder.Services.AddHttpClient<IBirdsService, BirdsService>(client =>
{
    client.BaseAddress = new Uri(configuration.GetValue<string>("Endpoint:UrlBirds"));
    client.Timeout = TimeSpan.FromSeconds(20);
    client.DefaultRequestHeaders.Add("HEADER_API_KEY", "claveficticia");
});
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();


Enter fullscreen mode Exit fullscreen mode
  • En el codigo primero traemos la configuración que permitirá traer la url desde appsettings.json


var configuration = builder.Configuration;


Enter fullscreen mode Exit fullscreen mode
  • En el siguiente bloque implementamos IHttpClientFactory


builder.Services.AddHttpClient<IBirdsService, BirdsService>(client =>
{
    client.BaseAddress = new Uri(configuration.GetValue<string>("Endpoint:UrlBirds"));
    client.Timeout = TimeSpan.FromSeconds(20);
    client.DefaultRequestHeaders.Add("HEADER_API_KEY", "claveficticia");
});


Enter fullscreen mode Exit fullscreen mode
  • Con el metodo AddHttpClient le indicamos la interfaz y la clase donde inyectaremos el HttpClient.

  • Luego vamos a asignar tres propiedades BaseAddress, Tiemout, DefaultRequestHeaders

    • BaseAddress: asignamos la url.
    • Timeout: asignamos el máximo tiempo de respuesta de una solicitud.
    • DefaultRequestHeaders: Agregamos una cabecera extra a nuestro Request, que para efectos prácticos puede ser una api key.

Llamando al servicio:

Tenemos una interfaz y una clase en la carpeta Services

IBirdsService:



using HttpClientApi.Models;

namespace HttpClientApi.Services
{
    public interface IBirdsService
    {
        Task<List<Bird>> Get();
    }
}


Enter fullscreen mode Exit fullscreen mode
  • La interfaz con el metodo Get nos permitirá implementar la inyección de dependencia.

BirdsService:



using HttpClientApi.Models;

namespace HttpClientApi.Services
{
    public class BirdsService : IBirdsService
    {
        private readonly HttpClient _httpClient;

        public BirdsService(HttpClient httpClient) => (_httpClient) = (httpClient);

        public async Task<List<Bird>> Get()
        {
            var listBirds = await _httpClient.GetFromJsonAsync<List<Bird>>(_httpClient.BaseAddress);

            return listBirds;
        }
    }


Enter fullscreen mode Exit fullscreen mode
  • La clase BirdsService implementa la interfaz IBirdsService.
  • En el constructor declaramos HttpClient httpClient para que pueda ser inyectado desde el Program.cs como lo vimos en el codigo de mas arriba.

Listo ya tenemos el servicio conectado solo nos falta llamarlo desde el controlador BirdsController



using HttpClientApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace HttpClientApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BirdsController : ControllerBase
    {
        private readonly IBirdsService _birdsService;

        public BirdsController(IBirdsService birdsService) => _birdsService = birdsService;

        [HttpGet]
        public async Task<IActionResult> Get()
        {
            return Ok(await _birdsService.Get());
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Y listo ahora ya estamos en condiciones de realizar el get  Birds

Conclusiones

  • Como vimos en el ejemplo y por la experiencia que tengo utilizándolo es mucho mas limpio con AddHttpCLient. Por que como vimos en la clase BirdsService en ningún momento declaramos la url, el timeout o si fuera necesario alguna cabecera adicional.
  • Le damos la responsabilidad a la inyección de dependencia que lo haga.
  • Para agregar IHttpClientFactory en este ejemplo lo hicimos directo en el Program.cs pero si quisieras dejar este archivo mas limpio podemos alojar esta configuración en otro archivo .cs, con la implementación ya lista como un metodo estático y solo la llamamos desde Program.cs. Esto es una buena opción cuando necesitas inyectar distintos servicios, es ahi donde nuestro código se nos puede extender.
  • Administra la agrupación y la duración de las instancias de HttpClientMessageHandler subyacentes. La administración automática evita los problemas comunes de DNS (Sistema de nombres de dominio) que se producen al administrar la duración de HttpClient de forma manual. Esto quiere decir que los problemas que indicamos al principio de este post, que nos provocaba trabajar con HttpClient de forma manual del riesgo de solicitudes que queden abiertas y colapso de los sockets, ya no existirían.
💖 💪 🙅 🚩
airarrazabald
Abelardo Ruben Irarrázabal Díaz

Posted on October 21, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related