ASP. NET Core - Usando HttpClientFactory - I
![]() |
Hoje vamos recordar como usar o recurso HttpClientFactory em aplicações ASP .NET Core. |
Eu já apresentei a classe HttpClientFactory neste artigo - Usando HttpClientFactory - e hoje veremos como podemos usar este recurso em aplicações ASP .NET Core no ambiente do .NET 6.
![]() |
![]() |
Para mostrar isso na prática vamos usar uma aplicação ASP .NET Core Web API chamada ApiReservas que já esta pronta e que eu mostro como criar neste artigo : ASP .NET Core - Criando uma Web API - I
Os endpoints expostos por esta API são:
HTTP | URL | Dados do cliente | Retorno |
---|---|---|---|
GET | /api/reservas | Sem dados | Todas as resrevas no formato JSON |
GET | /api/reservas/1, /api/reservas/2, etc | Sem dados | A reserva do id que foi enviado como parâmetro |
POST | /api/reservas | O objeto reserva no formato JSON | A nova reserva criada no formato JSON |
PUT | /api/reservas | O objeto reserva no formato JSON | A nova reserva atualizada no formato JSON |
DELETE | /api/reservas/1, /api/reservas/2, etc | Sem dados | Nada |
PATCH | /api/reservas/1, /api/reservas/2, etc | Um JSON que contém um conjunto de alterações a serem aplicadas | A confirmação que a alteração foi aplicada. |
Seguindo o roteiro mostrado no artigo eu recriei o projeto usando o .NET 6 no VS 2022 onde defini o nome da Solution UsandoHttpClientFactory. Este será o nosso ponto de partida.
A seguir vamos criar uma aplicação ASP .NET Core MVC e consumir esta API usando os recursos de HttpClientFactory.
Para criar a aplicação MVC inclua na solução um novo projeto usando o template ASP .NET Core Web App (Model-View-Controller) e informe o nome ReservasWeb.
Vamos configurar a nossa Solution para iniciar os dois projetos quando for executada e assim teremos a API em atendimento e a aplicação MVC pronta para consumir a API:
Temos assim uma Solução contendo o projeto Web API que esta pronta e o projeto MVC que iremos usar para consumir a API usando o HttpClientFactory.
Consumindo a API com HttpClientFactory
Vamos agora preparar a aplicação ASP .NET Core MVC para consumir os endpoints da Web API ApiReservas.
Vamos criar na pasta Models a classe Reserva:
public class Reserva { public int ReservaId { get; set; } public string? Nome { get; set; } public string? InicioLocacao { get; set; } public string? FimLocacao { get; set; } } |
Vamos incluir no projeto o pacote Nuget Microsoft.Extensions.Http usando o comando Install-Package Microsoft.Extensions.Http na janela do Package Manager Console.
A seguir vamos adicionar o serviço de IHttpClientFactory no contêiner DI nativo incluindo na classe Program o código a seguir:
... builder.Services.AddHttpClient("Reservas", httpClient=> { httpClient.BaseAddress = new Uri("https://localhost:7211/"); }); ... |
Com isso estamos definido o que é conhecido como named HttpClient ou cliente nomeado, onde já definimos a propriedade BaseAddress para acessar a URL onde a API ApiReservas esta em atendimento.
A seguir vamos criar na pasta Controllers o controlador ReservasController e vamos injetar uma instância de IHttpClientFactory no construtor :
public class ReservasController : Controller { private readonly IHttpClientFactory _clientFactory; private readonly JsonSerializerOptions _options; public ReservasController(IHttpClientFactory httpClientFactory) { _clientFactory = httpClientFactory; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; } .... } |
Definimos também a propriedade PropertyNameCaseInsensitive como true para indicar que o nome das propriedades usa uma comparação que não é case sensitive durante a serialização.
Precisamos definir também o endpoint para acessar a API pois temos apenas o endereço base; para a nossa API os endpoints são /api/reservas/ :
public class ReservasController : Controller { private readonly IHttpClientFactory _clientFactory; private readonly JsonSerializerOptions _options; private const string apiUrl = "/api/reservas/"; public ReservasController(IHttpClientFactory httpClientFactory) { _clientFactory = httpClientFactory; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; } .... } |
Vamos criar duas propriedades listaReservas e reserva para tratar com um objeto Reserva e com uma lista de objetos Reserva:
public class ReservasController : Controller { private readonly IHttpClientFactory _clientFactory; private readonly JsonSerializerOptions _options; private const string apiUrl = "/api/reservas/"; public ReservasController(IHttpClientFactory httpClientFactory) { _clientFactory = httpClientFactory; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; }
public IEnumerable<Reserva>? listaReservas { get; set; } |
Agora podemos iniciar a criação dos métodos Action do Controlador começando com o método Index que vai retornar uma lista de reservas existentes.
Para isso vamos incluir o código abaixo no controlador ReservasController:
public async Task<IActionResult> Index() { var client = _clientFactory.CreateClient("Reservas"); var response = await client.GetAsync(apiUrl);
if (response.IsSuccessStatusCode) |
Neste código criamos um método Action Index assíncrono (async) e que retorna um Task<IActionResult>, onde usamos um named HttpClient - Reservas - para obter uma instância e realizar uma requisição assíncrona usando await, e o método GetAsync() usando a Url da API. A resposta será armazenada na variável response.
Como a resposta esta no formato JSON, para poder desserializar facilmente o JSON para uma lista de objetos Reserva, usamos a biblioteca System.Text.Json e o código:
var apiResponse = await response.Content.ReadAsStreamAsync();
listaReservas
= await JsonSerializer.DeserializeAsync<IEnumerable<Reserva>>(apiResponse, _options)
A lista de objetos reserva armazenados na variável "listaReservas" serão retornados à view Index como um model.
Ajustando o arquivo _Layout.cshtml
Vamos ajustar o arquivo _Layout.cshtml para exibir um link para acessar as reservas. Para isso altere o código deste arquivo incluindo as linhas de código em azul, conforme mostrado a seguir:
...
<div class="navbar-collapse collapse d-sm-inline-flex
flex-sm-row-reverse"> |
Criando o arquivo de leiaute das
views
Agora temos que criar a view
Index na pasta Views/Reservas para exibir a
lista de reservas existente.
Antes disso vamos criar uma partial view chamada _reservaLayout.cshtml na pasta /Views/Shared onde vamos definir o leiaute das todas as views usando o código abaixo neste arquivo:
<!DOCTYPE html>
<html> |
Agora todas as views do nosso controlador ReservasController vão usar esse leiaute.
Clique com o botão direto do mouse no método Action Index e a seguir em Add View;
Na janela Add New Scaffolded Item selecione Razor View e clique em Add;
A seguir informe os dados conforme mostra a figura:
Note que estamos definindo como página de leiaute o nosso arquivo de leiaute: _reservaLayout.cshtml.
Criando a view Index : Exibindo uma lista de reservas
A seguir inclua o código no arquivo Index.cshtml gerado na pasta /Views/Reservas :
@model
IEnumerable<Reserva> @{ ViewData["Title"] = "Todas as Reservas"; Layout = "~/Views/Shared/_reservaLayout.cshtml"; }
<a asp-action="AddReserva"
class="btn btn-sm btn-primary">Incluir Reserva</a>
<table class="table table-sm
table-striped table-bordered m-2"> |
Este código usa os recursos do Bootstrap para definir a view que mostra a lista de reservas. A view é fortemente tipada recebendo um tipo IEnumerable<Reserva> como seu model e usar o laço foreach para popular a tabela com os dados.
Executando o projeto MVC (lembre-se que o projeto API deve estar em execução) iremos obter o seguinte resultado:
Acessando o link Reservas estaremos enviando um request usando o HttpClientFactory para consumir a API e o resultado obtido será a lista de reservas conforme mostra a figura a seguir:?
Obtendo uma reserva pelo Id usando um Typed Client
Para obter a lista de reservas definimos um método Action e usamos o HttpClientFactory conhecido como named client. Vamos agora retornar uma reserva pelo seu Id e vamos usar o HttpClientFactory conhecido como typed client.
Para isso vamos criar uma pasta Reservas no projeto e criar a interface IReservasClient e sua implementação na classe ReservasClient:
1- IReservasClient
public interface IReservasClient { Task<Reserva> GetReservaById(int id); } |
2- ReservasClient
public class ReservasClient : IReservasClient { public HttpClient _client; private const string apiUrl = "/api/reservas/"; public ReservasClient(HttpClient client) { _client = client; _client.BaseAddress = new Uri("https://localhost:7211/"); _client.Timeout = new TimeSpan(0, 0, 50); } public async Task<Reserva> GetReservaById(int id) |
Observe que no código da classe já definimos o cliente HttpClient e definimos o endereço base e o método que vai retornar a reserva pelo seu Id usando a instância do HttpClient e o método GetFromJsonAsync.
A seguir vamos registrar o serviço para esta interface na classe Program:
...
builder.Services.AddHttpClient<IReservasClient, ReservasClient>();
... |
A seguir para implementar a funcionalidade de obter uma reserva pelo seu Id no controlador ReservasController criando os métodos Action GetReserva para o GET e a seguir para o POST.
Antes precisamos injetar a interface IReservasClient do cliente tipado que definimos no construtor do Controlador:
public class ReservasController : Controller { private readonly IHttpClientFactory _clientFactory; private const string apiUrl = "/api/reservas/"; private readonly JsonSerializerOptions _options; private readonly IReservasClient _reservasClient; public ReservasController(IHttpClientFactory httpClientFactory, IReservasClient reservasClient) { _clientFactory = httpClientFactory; _reservasClient = reservasClient; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; } public IEnumerable<Reserva>? listaReservas { get; set; } public Reserva? reserva { get; set; } ... } |
A versão GET da Action apenas retorna a View.
Já a versão POST faz uma requisição para a API para obter a reserva passando o Id da reserva usando a instância do cliente tipado _reservasClient que foi injetado no controlador:
public ViewResult GetReserva() => View();
[HttpPost] |
Com isso encapsulamos na classe ReservasClient do cliente tipado as funcionalidades necessárias para consumir a API.
Vamos então criar a view ‘GetReserva’ na pasta ‘Views/Reservas’ com o código abaixo:
@model Reserva
@{ <h2>Obter reserva pelo Id <a asp-action="Index" class="btn btn-sm btn-success">Retornar</a></h2>
<form method="post">
@if (Model != null) |
Esta view temos um formulário onde podemos informar o Id da reserva e clicar no botão para enviar uma requisição para a web api.
A expressão @if (Model != null) verifica se o model não é null e exibe os dados obtidos da API.
Executando o projeto novamente e clicando em Obter Reserva teremos o seguinte resultado:
No próximo artigo vamos continuar consumindo a API criando os demais métodos Action.
"Ele(Jesus)
respondeu: "Cuidado para não serem enganados. Pois muitos virão em meu nome,
dizendo: ‘Sou eu! ’ e ‘o tempo está próximo’. Não os sigam."
Lucas 21:8
Referências: