ASP .NET Core - Implementando HATEOAS
Neste artigo veremos uma implementação da funcionalidade HATEOAS em uma API ASP .NET Core. |
Vamos continuar a primeira parte do artigo onde eu apresentei o HATEOAS e agora mostrar uma forma de implementar esta funcionalidade em uma API ASP .NET Core.
Vou usar um projeto Web API já pronto para focar apenas na implementação HATEOAS, e, vou fazer uma implementação bem simples.
Nossa Web API ClientesAPI expõe serviços para consultar, incluir, alterar e excluir informações de clientes e define um controlador chamado ClientesController que usa o EF Core para realizar o acesso e a persistência dos dados.
A seguir vamos fazer a nossa implementação realizando as seguintes tarefas:
Implementando HATEOAS
Abra o projeto ClientesAPI no VS 2019 Community e na pasta Models do projeto crie a classe LinkDTO:
namespace ClientesApi.Model
{
public class LinkDTO
{
public int Id { get; private set; }
public string Href { get; private set; }
public string Rel { get; private set; }
public string Metodo { get; private set; }
public LinkDTO(string href, string rel, string metodo)
{
Href = href;
Rel = rel;
Metodo = metodo;
}
}
}
|
A seguir ainda na pasta Models crie a classe Recurso :
public class Recurso { public List<LinkDTO> Links { get; set; } = new List<LinkDTO>(); } |
Altere a classe Cliente de forma a que passe herdar da classe Recurso:
public class Cliente : Recurso
{
public int ClienteId { get; set; }
public string Nome { get; set; }
public string Email { get; set; }
public string Telefone { get; set; }
public string Endereco { get; set; }
}
|
Nossa implementação vai usar os recursos da classe UrlHelper e para isso vamos definir um serviço IUrlHelper para poder injetar uma instância do serviço no construtor do controlador.
Para isso vamos registrar o serviço no método ConfigureServices da classe Startup:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<ClienteDbContext>(opt => opt.UseMySql(Configuration.GetConnectionString("ClientesConnectionString"))); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//Implementação de
IUrlHelper para injetar a instância de UrlHelper |
Agora podemos nos concentrar no controlador ClientesController, onde vamos injetar uma instância de UrlHelper e definir o método GerarLinks().
1- Injetando o serviço UrlHelper no construtor
...
[Route("api/[controller]")]
[ApiController]
public class ClientesController : ControllerBase
{
private readonly ClienteDbContext _context;
private readonly IUrlHelper _urlHelper;
public ClientesController(ClienteDbContext context, IUrlHelper urlHelper)
{
_context = context;
_urlHelper = urlHelper;
}
...
|
Agora vamos definir o método GerarLinks():
private
void GerarLinks(Cliente cliente) { cliente.Links.Add(new LinkDTO(_urlHelper.Link(nameof(GetCliente), new { id = cliente.ClienteId }), rel: "self", metodo: "GET")); cliente.Links.Add(new LinkDTO(_urlHelper.Link(nameof(PutCliente), new { id = cliente.ClienteId }), rel: "update-cliente", metodo: "PUT")); cliente.Links.Add(new LinkDTO(_urlHelper.Link(nameof(DeleteCliente), new { id = cliente.ClienteId }), rel: "delete-cliente", metodo: "DELETE")); } |
Este método recebe uma instância de Cliente e usa os recursos da classe UrlHelper para gerar os links para os métodos GET, PUT e DELETE.
Para concluir vamos ter que incluir um nome qualificado para cada método Action conforme definimos no método acima.
Para testar vamos incluir uma chamada a GerarLinks no método Action Get(int id) : GerarLinks(cliente);
Executando o projeto e acionando a url : https://localhost:44345/api/clientes/1 vemos o resultado conforme mostrado a seguir:
Falta implementar a geração dos links para a lista de clientes onde vamos ter que gerar o links para os métodos Action e o link que indica a possibilidade para criar clientes em cada cliente.
Para isso vamos criar na pasta Models a classe ColecaoRecursos<T> que herda da classe Recurso :
using System.Collections.Generic;
namespace ClientesApi.Model |
Agora no controlador ClientesController vamos alterar o método Action Get() conforme abaixo:
// GET:
api/Clientes [HttpGet(Name = nameof(GetClientes))] public async Task<ActionResult<ColecaoRecursos<Cliente>>> GetClientes() { var clientes = await _context.Clientes.ToListAsync(); clientes.ForEach(c => GerarLinks(c));
var
resultado = new ColecaoRecursos<Cliente>(clientes);
return
resultado; |
Executando o projeto novamente agora veremos o resultado a seguir para a url: https://localhost:44345/api/clientes
Pegue o código do projeto neste link: ClientesApi_hateoas.zip
"Porque
onde há inveja e espírito faccioso aí há perturbação e toda a obra perversa.
Mas a sabedoria que do alto vem é, primeiramente pura, depois pacífica,
moderada, tratável, cheia de misericórdia e de bons frutos, sem parcialidade, e
sem hipocrisia."
Tiago 3:16,17
Referências:
http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http
http://symfony.com/video/26/designing-http-interfaces-and-restful-web-services/English
REST - Compreendendo o REST
JSON - Introdução e conceitos básicos
C# - Converter de JSON para Object e vice-versa
NET - Apresentando os fundamentos do REST
ASP .NET Web API - Criando e Consumindo uma Web API
Node - Criando uma API REST com Express ...
VB .NET - Consumindo um Web Service JSON ...