Imagine a situação, é necessário criar uma API que será consumida pela versão web e versão mobile da aplicação. A versão web deve mostrar todas as informações dos dados, já a versão mobile irá exibir apenas as informações mais importantes. Neste cenário, o desenvolvedor pode optar por endpoints distintos ou mesmo necessitar implementar filtros complexos. Entretanto, todo este processo pode ser facilitado com o auxílio do OData.
Curso C# (C Sharp) - Introdução ao ASP.NET Core
Conhecer o cursoOData
Open Data Protocol (OData) é um padrão que define boas práticas para a criação e consumo de APIs RESTful. Ele ajuda o desenvolvedor a focar nas regras de negócio da API, sem ter que se preocupar com a implementação de aspectos técnicos como media types, formatos de payload, opções de query, etc.
O seu uso requer apenas alterações pontuais no código, como veremos à seguir.
Integrando o OData a uma API ASP.NET Core
Para exemplificar o uso do OData criaremos uma API ASP.NET Core simples:
dotnet new webapi -n PessoaAPI
Que retornará apenas um recurso:
public class Pessoa
{
public int Id { get; set; }
public string Nome { get; set; }
public int Idade { get; set; }
public override string ToString() => Id + " - " + Nome + " - " + Idade;
}
Salvo na memória:
public class PessoaRepository
{
private static Dictionary<int, Pessoa> pessoas = new Dictionary<int, Pessoa>();
public List<Pessoa> GetAll(){
return pessoas.Values.ToList();
}
public Pessoa Get(int id){
return pessoas.GetValueOrDefault(id);
}
public void Add(Pessoa pessoa){
pessoas.Add(pessoa.Id, pessoa);
}
public void Edit(Pessoa pessoa){
pessoas.Remove(pessoa.Id);
pessoas.Add(pessoa.Id, pessoa);
}
public void Delete(int id){
pessoas.Remove(id);
}
}
Com isso, terá apenas um controller:
[ApiController]
[Route("[controller]")]
public class PessoaController : ControllerBase
{
private readonly ILogger<PessoaController> _logger;
private readonly PessoaRepository _repository;
public PessoaController(ILogger<PessoaController> logger, PessoaRepository repository)
{
_logger = logger;
_repository = repository;
}
[HttpGet]
public IEnumerable<Pessoa> Get()
{
var pessoas = _repository.GetAll();
return pessoas;
}
[HttpGet("{id}")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
public ActionResult<Pessoa> Get(int id)
{
var item = _repository.Get(id);
if(item != null)
return item;
return NotFound();
}
[HttpPost]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
public ActionResult<Pessoa> Post([FromBody] Pessoa value)
{
try
{
_repository.Add(value);
return value;
}
catch(Exception)
{
return BadRequest();
}
}
[HttpPut("{id}")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
public ActionResult<Pessoa> Put(int id, [FromBody] Pessoa value)
{
try
{
value.Id = id;
_repository.Edit(value);
return value;
}
catch(Exception)
{
return BadRequest();
}
}
[HttpDelete("{id}")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
public ActionResult Delete(int id)
{
try
{
_repository.Delete(id);
return Ok(new { Description = "Item removed" });
}
catch(Exception)
{
return BadRequest();
}
}
}
Para integrar o OData na aplicação, basta adicionar no projeto a dependência Microsoft.AspNetCore.OData
:
dotnet add package Microsoft.AspNetCore.OData
Também adicione a dependência Microsoft.AspNetCore.Mvc.NewtonsoftJson
:
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
No método ConfigureServices
, o serviço do OData deve ser adicionado:
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<PessoaRepository>();
services.AddControllers()
.AddNewtonsoftJson();
//Adiciona o OData
services.AddOData();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "PessoaAPI", Version = "v1" });
});
}
Note que o AddNewtonsoftJson()
não é adicionado. Isso é necessário porque o OData não funciona corretamente com o System.Text.Json.
E no método Configure
, na configuração dos endpoints da aplicação, é necessário configurar o middleware do OData:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Filter().OrderBy();
});
O EnableDependencyInjection()
permite a sobrescrita das URLs, desta forma não é necessário especificar uma separada para o OData.
O OData pode aplicar filtro, seleção, ordenação e outros recursos ao retorno da API, acima são habilitados a seleção, filtro e ordenação.
Entretanto, para que o OData funcione, por fim, é necessário adicionar o atributo EnableQuery
nos métodos dos controllers onde ele será aplicado:
[HttpGet]
[EnableQuery]
public IEnumerable<Pessoa> Get()
{
var pessoas = _repository.GetAll();
return pessoas;
}
Caso tenha implementado Entity Framework e o DbContext é acessado no controller, substitua o IEnumerable
pelo IQueryable
. Assim, o OData irá utilizar as capacidades do Entity Framework para aplicar as seleções.
Testando o OData na aplicação
Com o OData implementado na aplicação podemos testá-lo, para isso, a aplicação precisa conter alguns dados:
Neles, é possível aplicar uma seleção com o atributo $select
na querystring do endpoint (pessoa?$select=nome
):
Pode ser informado mais de uma propriedade, separando elas por vírgula (e.g.pessoa?$select=nome,idade
).
O mesmo processo se aplica a ordenação, com o uso do atributo $orderBy
(pessoa?$orderBy=idade
):
Já para o filtro, pode ser aplicado uma das condicões abaixo:
Condição | Descrição | Exemplo |
---|---|---|
eq | Igual | $filter=idade eq 18 |
ne | Diferente | $filter=idade ne 18 |
gt | Maior | $filter=idade gt 18 |
ge | Maior ou igual | $filter=idade ge 18 |
lt | Menor | $filter=idade lt 18 |
le | Menor ou igual | $filter=idade le 18 |
and | E lógico | $filter=idade gt 18 and idade lt 30 |
or | Ou lógico | $filter=idade gt 18 or idade lt 30 |
not | Negação lógica | $filter=not endswith(nome,‘Silva’) |
Por exemplo (pessoa?$filter=idade gt 30
):
Por fim, também é possível combinar mais de um atributo na querystring (pessoa?$select=nome&$filter=not endswith(nome,'Silva')
):
Curso C# (C Sharp) - APIs REST com ASP.NET Web API
Conhecer o cursoConclusão
Este artigo apenas introduz o OData, ele possui muitos recursos e merece sua atenção caso esteja trabalhando com APIs RESTful no ASP.NET Core.
Caso a API retorne uma grande quantidade de dados, a ordenação ($orderBy
) e filtro ($filter
) podem ser muito custosas, então nesta situação é necessário analisar se suas habilitações compensam. Se não, recomenda-se deixá-los inativos.
Nos próximos artigos abordei mais recursos do OData, até lá!