C# ASP .NET .NET Core

C# - Consumindo APIs com Flurl

Veja como consumir dados de uma API no C#, utilizando a biblioteca Flurl.

há 3 anos 2 meses

Formação Full-stack: Desenvolvedor ASP.NET
Conheça a formação em detalhes

Com a diversificação do acesso, está se tornando um padrão a criação de back-end, APIs, que posteriormente serão consumidas por outras aplicações. Por isso, atualmente é imprescindível saber realizar este procedimento. Felizmente, no C#, a biblioteca Flurl facilita, e muito, este processo.

Flurl

Criada por Todd Menier, Flurl é uma biblioteca open source para .NET. Ela se define como um builder de URL, moderno, assíncrono, fluent, portável, testável, entre outras buzzword e uma biblioteca de requisições HTTP.

De forma simples, ela nos permite criar requisições HTTP que são facilmente testáveis. Entretanto, não abordaremos este detalhe aqui. Neste arquivo, veremos apenas a criação de requisições.

ASP.NET Core - Fundamentos
Curso ASP.NET Core - Fundamentos
Conhecer o curso

Aplicação base

Para exemplificar o uso da biblioteca, irei utilizar como base a aplicação demonstrada no artigo da biblioteca RepoDB e o pacote Tw Dev Server do Akira Hanashiro.

Consumindo dados da API

No momento, ao executar a aplicação, os produtos são listados do banco:

Iremos alterá-la pra que esses dados sejam obtidos pela API.

Por esta aplicação adotar o padrão repository, as principais alterações serão realizadas nesta camada. As demais sofrerão apenas adaptações pontuais.

A primeira coisa à ser feita é adicionar a biblioteca Flurl:

dotnet add package Flurl.Http

Por ser uma biblioteca fluent, seus métodos são de extensão. Assim, inicialmente iremos definir a url:

 const string url = "http://localhost:3002/api/products";

Em seguida iremos alterar o método FindAll, que no momento tem o conteúdo abaixo:

public IEnumerable<Product> FindAll()
{
    return QueryAll();
}

A obtenção de dados é feita através de uma requisição GET. Como os dados da nossa API são retornados como JSON, a Flurl possui o método de extensão GetJsonAsync que já cria esta requisição e parseia os dados:

public async Task<IEnumerable<Product>> FindAll()
{
    return await url.GetJsonAsync<List<Product>>();
}

Como GetJsonAsync é assíncrono, note que foi necessário definir o FindAll() como assíncrono. Com isso, será necessário modificar a interface:

Task<IEnumerable<T>> FindAll();

E o controller:

public async Task<ActionResult> Index()
{
    return View((await productRepository.FindAll()).ToList());
}

Também será necessário modificar o model, porque os ids criados pela API são GUID:

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int Quantity { get; set; }
    public double Price { get; set; }
}

Como a API não possui nenhum dado, por enquanto a listagem não mostra nada:

Listagem de produtos com nenhum item

Enviando dados para a API

Com a listagem pronta, vamos enviar dados para a API. Isso é feito via uma requisição POST. Assim, como a GET, a biblioteca possui o método de extensão PostJsonAsync que já facilita a criação desta requisição:

public async Task Add(Product item)
{
    await url.PostJsonAsync(item);
}

Também é necessário modificar a interface:

Task Add(T item);

E o controller:

public async Task<ActionResult> Create([Bind("Id,Name,Quantity,Price")] Product product)
{
    if (ModelState.IsValid)
    {
        await productRepository.Add(product);
        return RedirectToAction("Index");
    }

    return View(product);
}

Agora podemos adicionar dados na API e consumi-los:

Listagem de produtos com um item

Atualizando dados da API

O processo de atualização ocorre em duas etapas. Inicialmente é necessário obter os dados do registro que será atualizado. Para isso, deve ser feita uma requisição GET, passando o id do registro:

public async Task<Product> FindByID(string id)
{
    return await url
                .SetQueryParams(new { id = id })
                .GetJsonAsync<Product>();
}

Note que estamos utilizando o método SetQueryParams passar o id via querystring. No final, teremos uma URL no seguinte formato: http://localhost:3002/api/products?id=<valor id>.

Assim como antes, também é necessário mudar a interface:

Task<T> FindByID(string id);

E o controller:

public async Task<ActionResult> Edit(string id)
{
    if (id == null)
    {
        return StatusCode(StatusCodes.Status400BadRequest);
    }
    Product product = await productRepository.FindByID(id);
    if (product == null)
    {
        return StatusCode(StatusCodes.Status404NotFound);
    }
    return View(product);
}

Aproveite e altere o método de detalhes:

public async Task<ActionResult> Details(string id)
{
    if (id == null)
    {
        return StatusCode(StatusCodes.Status404NotFound);
    }
    Product product = await productRepository.FindByID(id);
    if (product == null)
    {
        return StatusCode(StatusCodes.Status404NotFound);
    }
    return View(product);
}

Agora ao clicar no link de edição (ou detalhes), os dados do produto serão mostrados:

Tela de edição mostrando os detalhes de um produto

A atualização dos dados é realizada via uma requisição PUT e como deve imaginar, a Flurl também fornece um método de extensão que facilita a implementação desta requisição:

public async Task Update(Product item)
{
    await url
            .SetQueryParams(new { id = item.Id })
            .PutJsonAsync(item);
}

Note que também é necessário passar o ID via querystring, pois esta é uma especificação da API.

Não se esqueça de mudar a interface:

Task Update(T item);

E o controller:

public async Task<ActionResult> Edit([Bind("Id,Name,Quantity,Price")] Product product)
{
    if (ModelState.IsValid)
    {
        await productRepository.Update(product);
        return RedirectToAction("Index");
    }
    return View(product);
}

Agora, poderemos alterar os nossos registros:

Listagem de produtos exibindo um produto alterado

Excluindo dados da API

Para finalizar, falta implementar apenas a exclusão dos dados. Isso é feito via uma requisição DELETE, que pode ser implementada via o método de extensão DeleteAsync:

public async Task Remove(string id)
{
    await url
            .SetQueryParams(new { id = id })
            .DeleteAsync();
}

Não se esqueça que é necessário alterar a interface:

Task Remove(string id);

E o controller:

public async Task<ActionResult> DeleteConfirmed(string id)
{
    await productRepository.Remove(id);
    return RedirectToAction("Index");
}

Ao remover o único registro da nossa lista, ela voltará a ficar vazia:

Listagem de produtos com nenhum item

C# (C Sharp) - APIs REST com ASP.NET Web API
Curso C# (C Sharp) - APIs REST com ASP.NET Web API
Conhecer o curso

Conclusão

Note que com poucas alterações, conseguimos alterar a fonte de dados da aplicação para uma API. E esta comunicação com a API foi facilitada graças a biblioteca Flurl.

Esta biblioteca fornece uma grande gama de recursos e opções que conheceremos em artigos futuros. Entretanto, caso necessite trabalhar com API no .NET, não deixe de dar uma olhada na sua documentação. Tenho certeza que ela irá facilitar, e muito, o seu trabalho.

Autor(a) do artigo

Wladimilson M. Nascimento
Wladimilson M. Nascimento

Instrutor, nerd, cinéfilo e desenvolvedor nas horas vagas. Graduado em Ciências da Computação pela Universidade Metodista de São Paulo.

Todos os artigos

Artigos relacionados Ver todos