ASP. NET Core - Usando HttpClientFactory - II


 Hoje vamos continuar a recordar como usar o recurso HttpClientFactory em aplicações ASP .NET Core.

Continuando o artigo anterior vamos criar os demais métodos Action para consumir a api ApiReservas.


Para os demais métodos Action vamos usar a abordagem do clientes nomeados da HttpClientFactory pois no nosso caso temos apenas uma única API e um modelo de domínio. Dessa forma vamos usar o cliente nomeado definido para retornar a lista de reservas e que foi definido na classe Program com o código abaixo:

builder.Services.AddHttpClient("Reservas", httpClient =>
{
      httpClient.BaseAddress = new Uri("https://localhost:7211/");
});

Criando uma nova Reserva (named client)

Para adicionar uma nova reserva, devemos fazer uma requisição HTTP POST para a web api ApiReservas usando o método PostAsync() da classe HttpClient onde usamos a instância de HttpClientFactory e o método CreateClient para criar a instância do cliente Http.

Para isso vamos criar o método Action AddReserva() no controlador onde teremos o GET e o POST.

    public ViewResult AddReserva() => View();

    [HttpPost]
    public async Task<IActionResult> AddReserva(Reserva reserva)
    {

        var client = _clientFactory.CreateClient("Reservas");

        StringContent content = new StringContent(JsonSerializer.Serialize(reserva),
                                               Encoding.UTF8, "application/json");

        using (var response = await client.PostAsync(apiUrl, content))
        {
            var apiResponse = await response.Content.ReadAsStreamAsync();
            reserva = await JsonSerializer.DeserializeAsync<Reserva>(apiResponse, _options);
        }

        return View(reserva);
    }

No método POST precisamos passar os dados da nova reserva no formato JSON, e para isso estamos serializando os dados da classe Reserva para JSON e depois convertendo-os em um objeto StringContent:

  StringContent content = new StringContent(JsonSerializer.Serialize(reserva),
                                              Encoding.UTF8, "application/json");

O objeto StringContent é então adicionado à requisição da API, adicionando-o ao segundo parâmetro do método PostAsync().

A API adicionará a nova reserva ao seu repositório e, em seguida, enviará de volta esse objeto de reserva, juntamente com o ID da reserva, como resposta.

A resposta é então desserializada para a classe Reserva e é enviada para a view como um model.

Agora basta criar a view 'AddReserva' na pasta 'Views/Reservas' com o seguinte código:

@model Reserva
@{
    ViewData["Title"] = "AddReserva";
    Layout = "~/Views/Shared/_reservaLayout.cshtml";
}

<h2>Nova Reserva <a asp-action="Index" class="btn btn-sm btn-outline-success">Retorna</a></h2>

<form asp-action="AddReserva" method="post">
    <div class="form-group">
        <label for="Name">Nome:</label>
        <input class="form-control" name="Nome" />
    </div>
    <div class="form-group">
        <label for="InicioLocacao">Início da Locação:</label>
        <input class="form-control" name="InicioLocacao" />
    </div>
    <div class="form-group">
        <label for="FimLocacao">Fim da Locação:</label>
        <input class="form-control" name="FimLocacao" />
    </div>
    <div class="text-center panel-body">
        <button type="submit" class="btn btn-sm btn-primary">Nova Reserva</button>
    </div>
</form>

@if (Model != null)
{
    <h2>Reserva</h2>
    <table class="table table-sm table-striped table-bordered m-2">
        <thead><tr><th>ID</th><th>Nome</th><th>Inicio</th><th>Fim</th></tr></thead>
        <tbody>
            <tr>
                <td>@Model.ReservaId</td>
                <td>@Model.Nome</td>
                <td>@Model.InicioLocacao</td>
                <td>@Model.FimLocacao</td>
            </tr>

        </tbody>
    </table>
}

Executando o projeto teremos o resultado abaixo:

Atualizando uma reserva via HttpPut usando named client

Para atualizar uma reserva podemos clicar no ícone da coluna Atualiza na view que exibe todas as reservas.

Para implementar a atualização vamos criar dois métodos Action UpdateReserva em ReservasController:

  1. UpdateReserva(int id) - GET
  2. UpdateReserva(Reserva reserva) - POST
   [HttpGet]
    public async Task<IActionResult> UpdateReserva(int id)
    {
        var client = _clientFactory.CreateClient("Reservas");

        using (var response = await client.GetAsync(apiUrl + id))
        {
            var apiResponse = await response.Content.ReadAsStreamAsync();
            reserva = await JsonSerializer.DeserializeAsync<Reserva>(apiResponse, _options);
        }
        return View(reserva);
    }

    [HttpPost]
    public async Task<IActionResult> UpdateReserva(Reserva reserva)
    {
        Reserva reservaRecebida = new Reserva();
        var client = _clientFactory.CreateClient("Reservas");

        var content = new MultipartFormDataContent();
        content.Add(new StringContent(reserva.ReservaId.ToString()), "ReservaId");
        content.Add(new StringContent(reserva.Nome), "Nome");
        content.Add(new StringContent(reserva.InicioLocacao), "InicioLocacao");
        content.Add(new StringContent(reserva.FimLocacao), "FimLocacao");

        using (var response = await client.PutAsync(apiUrl, content))
        {
            var apiResponse = await response.Content.ReadAsStreamAsync();
            ViewBag.Result = "Sucesso";
            reservaRecebida = await JsonSerializer.DeserializeAsync<Reserva>(apiResponse, _options);
        }
        return View(reservaRecebida);
    }

A versão Get simplesmente faz uma solicitação HTTP GET para a API e fornece o ID da reserva. A API enviará a ele a reserva obtida, que será vinculada ao formulário na view "UpdateReserva".

A versão Post faz a atualização do objeto Reserva. O argumento do tipo de reserva que ele possui é vinculado a partir do formulário na view "UpdateReserva".

Como o método PUT da API tem o atributo [FromForm] em seu argumento, estamos criando dados de formulário usando a classe MultipartFormDataContent, que será enviada à API como:

                var content = new MultipartFormDataContent();
                content.Add(new StringContent(reserva.ReservaId.ToString()), "ReservaId");
                content.Add(new StringContent(reserva.Nome), "Nome");
                content.Add(new StringContent(reserva.InicioLocacao), "InicioLocacao");
                content.Add(new StringContent(reserva.FimLocacao), "FimLocacao");


Nota: O atributo FromForm é usado para receber dados de um formulário enviado pelo tipo de conteúdo application/x-www-url-formencoded, enquanto FromBody analisa o modelo da maneira padrão, que na maioria dos casos usa o envio pelo tipo de conteúdo application/json, do body do request.

A API atualiza o registro reserva do ID que é enviado pelos dados do formulário,e , a resposta da API, que é o registro de reserva atualizado, é mostrado na view "UpdateReserva"

Vamos criar a view 'UpdateReserva' na 'Views/Reservas' com o seguinte código:

@model Reserva
@{
    ViewData["Title"] = "UpdateReserva";
    Layout = "~/Views/Shared/_reservaLayout.cshtml";
}

<h2>Atualiza uma Reserva <a asp-action="Index" class="btn btn-sm btn-success">Retorna</a></h2>
<form method="post">
    <div class="form-group">
        <label asp-for="ReservaId"></label>
        <input class="form-control" asp-for="ReservaId" readonly />
    </div>
    <div class="form-group">
        <label asp-for="Nome"></label>
        <input class="form-control" asp-for="Nome" />
    </div>
    <div class="form-group">
        <label asp-for="InicioLocacao"></label>
        <input class="form-control" asp-for="InicioLocacao" />
    </div>
    <div class="form-group">
        <label asp-for="FimLocacao"></label>
        <input class="form-control" asp-for="FimLocacao" />
    </div>
    <div class="text-center panel-body">
        <button type="submit" class="btn btn-sm btn-primary">Atualizar</button>
    </div>
</form>

@if (ViewBag.Result == "Sucesso")
{
    <h2>Reservas</h2>
    <table class="table table-sm table-striped table-bordered m-2">
        <thead><tr><th>ID</th><th>Nome</th><th>Início</th><th>Fim</th></tr></thead>
        <tbody>
            <tr>
                <td>@Model.ReservaId</td>
                <td>@Model.Nome</td>
                <td>@Model.InicioLocacao</td>
                <td>@Model.FimLocacao</td>
            </tr>

        </tbody>
    </table>
}

A nossa view possui um formulário para atualizar um registro de reserva específico. A resposta da API é mostrada dentro da tabela HTML.

Executando o projeto e selecionando uma reserva para atualizar teremos a reserva sendo exibida em um formulário para atualização:

Com isso estamos definido o que é conh

Deletando uma reserva via HttpDelete

Vamos agora incluir o método Action DeleteReserva no controlador ReservasController que vai deletar uma reserva a partir do repositório chamando o método DeleteAsync :

    [HttpPost]
    public async Task<IActionResult> DeleteReserva(int ReservaId)
    {
        var client =
_clientFactory.CreateClient("Reservas");

        using (var response = await client.DeleteAsync(apiUrl + ReservaId))
        {
            var apiResponse = await response.Content.ReadAsStreamAsync();
        }
        return RedirectToAction("Index");
    }

Este método Action possui um argumento ReservaId que é o id da reserva a ser deletada. Após deletar a reserva seremos redirecionados para a view Index.

O  código da view DeleteReserva.cshtml criado na pasta /Views/Reserva é o seguinte:

@model Reserva
@{
    ViewData["Title"] = "DeleteReserva";
    Layout = "~/Views/Shared/_reservaLayout.cshtml";
}
<form asp-action="DeleteReserva" method="post">
     <input type="hidden" value="@Model.ReservaId" name="ReservaId" />
     <input type="image" src="/icon/close.png" />
</form>

Executando o projeto, para deletar uma reserva basta clicar no ícone na coluna Deleta.

Concluímos assim a implementação de todos os métodos para consumir a nossa ApiReservas em nossa aplicação ASP .NET Core MVC : ReservasWeb usando HttpClientFactory e mostrando como usar a abordagem named client e typed client.

Pegue o código dos projetos aqui:    UsandoHttpClientFactory.zip (sem as referências)

"E o seu mandamento é este: que creiamos no nome de seu Filho Jesus Cristo, e nos amemos uns aos outros, segundo o seu mandamento."
1 João 3:23

Referências:

 


José Carlos Macoratti