APIs e Microsserviços

10 out, 2018

Criando uma API OData v4 com ASP.NET Core

100 visualizações
Publicidade

Fala, galera!

Hoje vamos falar um pouco sobre o OData (Open Data Protocol) e sobre como podemos criar APIs Rest utilizando este protocolo. Se você já desenvolveu APIs para diversos clientes, já deve ter tido o problema no qual um determinado campo no seu objeto não é necessário para um cliente, porém extremamente importante para outro cliente, ou até mesmo que sua API precisa de um filtro mais especializado para um determinado cliente, mas não é importante para outro.

Para estes tipos de cenários podemos utilizar o OData (Open Data Protocol), no qual o seu principal objetivo é criar APIs consultáveis, filtráveis e com interoperabilidade.

O que é Open Data Protocol (OData)?

OData ou Open Data Protocol é uma série de padrões para construção e consumo de API. Você pode saber mais sobre o OData Protocol através de sua documentação aqui. Existem inúmeros benefícios em utilizar o padrão OData, desde a facilidade de criar padrões de consultas até a facilidade de leitura do caminho de sua API.

Dentro do OData Protocol existem as suas convenções e umas de suas principais convenções é o Query Options – isso que torna nossas APIs flexíveis.

Query Options é usado para definir como sua API vai entregar os dados, podendo conter dados de pesquisas, filtros, ordenações, projeções e muito mais.

Abaixo um exemplo de uma API OData:

ASP.NET CORE E OData Protocol: como criar sua API OData

Vamos criar um API Rest com .NET Core utilizando o Protocolo OData. Para isso criaremos um projeto Web API conforme o exemplo abaixo:

Com o projeto criado vamos adicionar a referência do pacote do OData utilizando o Package Manager Console.

Install-Package Microsoft.AspNetCore.OData

Agora utilizaremos o EntityFramework Core como nosso ORM padrão e o SQL Server como provider para realizar a persistência dos dados. Para isso vamos adicionar o pacote EntityFrameworkCore.SqlServer conforme comando abaixo:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Vamos criar uma classe de Livro e assim utilizar o Entity Framework para mapear esta classe conforme o código abaixo:

public class Book
{
    [Key]
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string ISBN { get; set; }
    public string Author { get; set;}
}

Com a nossa classe de livro criada, vamos criar o nosso contexto. É nele ele que vamos consultar os dados do Livro conforme o código abaixo:

public class DataContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    public DataContext() : base()
    {
    }
    public DataContext(DbContextOptions options) : base(options)
    {
    }
}

Com o nosso contexto criado, vamos criar os nossos migrations. Para isso, no terminal digite:

Add-Migration Initial
Update-Database

Muito bem! Vamos agora configurar nossos serviços OData. Para isso, entre no Startup.cs e modifique-o conforme código abaixo:

public class Startup
{
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOData();
            services.AddDbContext<DataContext>(options =>
            {
                options.UseSqlServer(Configuration.GetConnectionString("ODataSample"));
            });
            services.AddMvc().AddJsonOptions(opt =>
            {
                opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseMvc(o =>
            {
                o.MapODataServiceRoute("ODataRoutes", "odata", GetEdmModel());
            });
        }
        public static IEdmModel GetEdmModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            
            // Habilita funções OData como $filter, $select e etc..
            builder.EntitySet<Book>(nameof(Book))
                   .EntityType
                   .Filter()
                   .Count()
                   .Expand()
                   .OrderBy()
                   .Page()
                   .Select();
            return builder.GetEdmModel();
        }
 }

Com o nosso Startup já configurado, basta agora criar um Controller. Este Controller será uma API OData, e para isso basta herdar da classe ODataController conforme o exemplo abaixo:

[Produces("application/json")]
[Route("api/Book")]
public class BookController : ODataController
{
    private DataContext Context { get; set; }
    public BookController(DataContext context)
    {
        this.Context = context ?? throw new ArgumentNullException(nameof(context));
    }
    [HttpPost]
    public IActionResult Post([FromBody] Book model)
    {
        this.Context.Books.Add(model);
        this.Context.SaveChanges();
        return Created("/api/book/" + model.Id, model);
    }
    [EnableQuery]
    public IQueryable<Book> Get() => Context.Books.AsQueryable();
}

Pronto. Nossa API OData está pronta, bastando somente a consumir. Para testa-lá vamos abrir o Postman e enviar algumas requisições conforme imagem abaixo:

Criando um novo registro

Obtendo todos os registros

Selecionando determinados campos e ordenando por Autor

E ai galera, viram como é fácil criar uma API OData com o ASP.NET Core? Com uma API OData podemos criar diversas consultas especializadas com os seus filtros, paginações, seleções e etc, e nossa API ganha um poder muito grande para trabalhar em cima de dados.

O código fonte deste exemplo encontra-se no meu GitHub através deste link.

Abraços e até a próxima!