Criando um ponto de extremidade OData v3 com a API Web 2
por Mike Wasson
O OData (Open Data Protocol ) é um protocolo de acesso a dados para a Web. O OData fornece uma maneira uniforme de estruturar dados, consultar os dados e manipular o conjunto de dados por meio de operações CRUD (criar, ler, atualizar e excluir). O OData dá suporte a formatos AtomPub (XML) e JSON. O OData também define uma maneira de expor metadados sobre os dados. Os clientes podem usar os metadados para descobrir as informações de tipo e as relações do conjunto de dados.
ASP.NET Web API facilita a criação de um ponto de extremidade OData para um conjunto de dados. Você pode controlar exatamente quais operações OData o ponto de extremidade dá suporte. Você pode hospedar vários pontos de extremidade OData, juntamente com pontos de extremidade não OData. Você tem controle total sobre seu modelo de dados, lógica de negócios de back-end e camada de dados.
Versões de software usadas no tutorial
- Visual Studio 2013
- API Web 2
- OData Versão 3
- Entity Framework 6
- Proxy de depuração da Web do Fiddler (opcional)
O suporte a OData da API Web foi adicionado na atualização ASP.NET and Web Tools 2012.2. No entanto, este tutorial usa scaffolding que foi adicionado em Visual Studio 2013.
Neste tutorial, você criará um ponto de extremidade OData simples que os clientes podem consultar. Você também criará um cliente C# para o ponto de extremidade. Depois de concluir este tutorial, o próximo conjunto de tutoriais mostra como adicionar mais funcionalidades, incluindo relações de entidade, ações e $expand/$select.
- Criar o Projeto do Visual Studio
- Adicionar um modelo de entidade
- Adicionar um controlador OData
- Adicionar o EDM e a Rota
- Propagar o banco de dados (opcional)
- Explorando o ponto de extremidade OData
- Formatos de serialização OData
Criar o Projeto do Visual Studio
Neste tutorial, você criará um ponto de extremidade OData que dá suporte a operações CRUD básicas. O ponto de extremidade exporá um único recurso, uma lista de produtos. Tutoriais posteriores adicionarão mais recursos.
Inicie o Visual Studio e selecione Novo Projeto na página Iniciar. Ou, no menu Arquivo , selecione Novo e, em seguida, Projeto.
No painel Modelos , selecione Modelos Instalados e expanda o nó Visual C#. Em Visual C#, selecione Web. Selecione o modelo ASP.NET Aplicativo Web .
Na caixa de diálogo Novo projeto ASP.NET , selecione o modelo Vazio . Em "Adicionar pastas e referências principais para...", marcar API Web. Clique em OK.
Adicionar um modelo de entidade
Um modelo é um objeto que representa os dados no seu aplicativo. Para este tutorial, precisamos de um modelo que represente um produto. O modelo corresponde ao nosso tipo de entidade OData.
No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Modelos. No menu de contexto, selecione Adicionar e, em seguida, selecione Classe.
Na caixa de diálogo Adicionar Novo Item, nomeie a classe "Produto".
Observação
Por convenção, as classes de modelo são colocadas na pasta Modelos. Você não precisa seguir essa convenção em seus próprios projetos, mas vamos usá-la para este tutorial.
No arquivo Product.cs, adicione a seguinte definição de classe:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
A propriedade ID será a chave de entidade. Os clientes podem consultar produtos por ID. Esse campo também seria a chave primária no banco de dados de back-end.
Compile o projeto agora. Na próxima etapa, usaremos alguns scaffolding do Visual Studio que usam reflexão para localizar o Tipo de produto.
Adicionar um controlador OData
Um controlador é uma classe que manipula solicitações HTTP. Você define um controlador separado para cada entidade definida em seu serviço OData. Neste tutorial, criaremos um único controlador.
No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores. Selecione Adicionar e, em seguida, selecione Controlador.
Na caixa de diálogo Adicionar Scaffold , selecione "Controlador OData da API Web 2 com ações, usando o Entity Framework".
Na caixa de diálogo Adicionar Controlador , nomeie o controlador como "ProductsController". Marque a caixa de seleção "Usar ações do controlador assíncrono". Na lista suspensa Modelo , selecione a classe Produto.
Clique no botão Novo contexto de dados... Deixe o nome padrão para o tipo de contexto de dados e clique em Adicionar.
Clique em Adicionar na caixa de diálogo Adicionar Controlador para adicionar o controlador.
Observação: se você receber uma mensagem de erro que diz "Houve um erro ao obter o tipo...", verifique se você criou o projeto do Visual Studio depois de adicionar a classe Product. O scaffolding usa reflexão para localizar a classe .
O scaffolding adiciona dois arquivos de código ao projeto:
- Products.cs define o controlador de API Web que implementa o ponto de extremidade OData.
- ProductServiceContext.cs fornece métodos para consultar o banco de dados subjacente usando o Entity Framework.
Adicionar o EDM e a Rota
Em Gerenciador de Soluções, expanda a pasta App_Start e abra o arquivo chamado WebApiConfig.cs. Essa classe contém o código de configuração para a API Web. Substitua esse código pelo seguinte:
using ProductService.Models;
using System.Web.Http;
using System.Web.Http.OData.Builder;
namespace ProductService
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
}
}
}
Esse código faz duas coisas:
- Cria um EDM (Modelo de Dados de Entidade) para o ponto de extremidade OData.
- Adiciona uma rota para o ponto de extremidade.
Um EDM é um modelo abstrato dos dados. O EDM é usado para criar o documento de metadados e definir os URIs para o serviço. O ODataConventionModelBuilder cria um EDM usando um conjunto de convenções de nomenclatura padrão EDM. Essa abordagem requer o código mínimo. Se você quiser mais controle sobre o EDM, poderá usar a classe ODataModelBuilder para criar o EDM adicionando propriedades, chaves e propriedades de navegação explicitamente.
O método EntitySet adiciona uma entidade definida ao EDM:
modelBuilder.EntitySet<Product>("Products");
A cadeia de caracteres "Products" define o nome do conjunto de entidades. O nome do controlador deve corresponder ao nome do conjunto de entidades. Neste tutorial, o conjunto de entidades é chamado de "Produtos" e o controlador é chamado ProductsController
de . Se você nomeou o conjunto de entidades como "ProductSet", nomeará o controlador ProductSetController
. Observe que um ponto de extremidade pode ter vários conjuntos de entidades. Chame EntitySet<T> para cada conjunto de entidades e defina um controlador correspondente.
O método MapODataRoute adiciona uma rota para o ponto de extremidade OData.
config.Routes.MapODataRoute("ODataRoute", "odata", model);
O primeiro parâmetro é um nome amigável para a rota. Os clientes do serviço não veem esse nome. O segundo parâmetro é o prefixo de URI para o ponto de extremidade. Dado esse código, o URI do conjunto de entidades Products é http:// hostname/odata/Products. Seu aplicativo pode ter mais de um ponto de extremidade OData. Para cada ponto de extremidade, chame MapODataRoute e forneça um nome de rota exclusivo e um prefixo de URI exclusivo.
Propagar o banco de dados (opcional)
Nesta etapa, você usará o Entity Framework para propagar o banco de dados com alguns dados de teste. Essa etapa é opcional, mas permite testar seu ponto de extremidade OData imediatamente.
No menu Ferramentas , selecione Gerenciador de Pacotes NuGet e, em seguida, Selecione Console do Gerenciador de Pacotes. Na janela Console do Gerenciador de Pacotes, digite o seguinte comando:
Enable-Migrations
Isso adiciona uma pasta chamada Migrações e um arquivo de código chamado Configuration.cs.
Abra esse arquivo e adicione o código a seguir ao Configuration.Seed
método .
protected override void Seed(ProductService.Models.ProductServiceContext context)
{
// New code
context.Products.AddOrUpdate(new Product[] {
new Product() { ID = 1, Name = "Hat", Price = 15, Category = "Apparel" },
new Product() { ID = 2, Name = "Socks", Price = 5, Category = "Apparel" },
new Product() { ID = 3, Name = "Scarf", Price = 12, Category = "Apparel" },
new Product() { ID = 4, Name = "Yo-yo", Price = 4.95M, Category = "Toys" },
new Product() { ID = 5, Name = "Puzzle", Price = 8, Category = "Toys" },
});
}
Na Janela do Console do Gerenciador de Pacotes, insira os seguintes comandos:
Add-Migration Initial
Update-Database
Esses comandos geram código que cria o banco de dados e, em seguida, executa esse código.
Explorando o ponto de extremidade OData
Nesta seção, usaremos o Proxy de Depuração da Web fiddler para enviar solicitações para o ponto de extremidade e examinar as mensagens de resposta. Isso ajudará você a entender os recursos de um ponto de extremidade OData.
No Visual Studio, pressione F5 para iniciar a depuração. Por padrão, o Visual Studio abre seu navegador para http://localhost:*port*
, em que a porta é o número da porta configurado nas configurações do projeto.
Você pode alterar o número da porta nas configurações do projeto. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Propriedades. Na janela de propriedades, selecione Web. Insira o número da porta em URL do Projeto.
Documento de Serviço
O documento de serviço contém uma lista dos conjuntos de entidades para o ponto de extremidade OData. Para obter o documento de serviço, envie uma solicitação GET para o URI raiz do serviço.
Usando o Fiddler, insira o seguinte URI na guia Compositor : http://localhost:port/odata/
, em que porta é o número da porta.
Clique no botão Executar . O Fiddler envia uma solicitação HTTP GET para seu aplicativo. Você deve ver a resposta na lista Sessões da Web. Se tudo estiver funcionando, o código status será 200.
Clique duas vezes na resposta na lista Sessões da Web para ver os detalhes da mensagem de resposta na guia Inspetores.
A mensagem de resposta HTTP bruta deve ser semelhante à seguinte:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/atomsvc+xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 17:51:01 GMT
Content-Length: 364
<?xml version="1.0" encoding="utf-8"?>
<service xml:base="http://localhost:60868/odata"
xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title type="text">Default</atom:title>
<collection href="Products">
<atom:title type="text">Products</atom:title>
</collection>
</workspace>
</service></pre>
Por padrão, a API Web retorna o documento de serviço no formato AtomPub. Para solicitar JSON, adicione o seguinte cabeçalho à solicitação HTTP:
Accept: application/json
Agora, a resposta HTTP contém uma carga JSON:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 22:59:28 GMT
Content-Length: 136
{
"odata.metadata":"http://localhost:60868/odata/$metadata","value":[
{
"name":"Products","url":"Products"
}
]
}
Documento de Metadados de Serviço
O documento de metadados de serviço descreve o modelo de dados do serviço, usando uma linguagem XML chamada CSDL (Linguagem de Definição de Esquema Conceitual). O documento de metadados mostra a estrutura dos dados no serviço e pode ser usado para gerar o código do cliente.
Para obter o documento de metadados, envie uma solicitação GET para http://localhost:port/odata/$metadata
. Aqui estão os metadados do ponto de extremidade mostrados neste tutorial.
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:05:52 GMT
Content-Length: 1086
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
<edmx:DataServices m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<Schema Namespace="ProductService.Models" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityType Name="Product">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" />
<Property Name="Price" Type="Edm.Decimal" Nullable="false" />
<Property Name="Category" Type="Edm.String" />
</EntityType>
</Schema>
<Schema Namespace="Default" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="Products" EntityType="ProductService.Models.Product" />
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
Conjunto de Entidades
Para obter a entidade Products definida, envie uma solicitação GET para http://localhost:port/odata/Products
.
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:01:31 GMT
Content-Length: 459
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products","value":[
{
"ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
},{
"ID":2,"Name":"Socks","Price":"5.00","Category":"Apparel"
},{
"ID":3,"Name":"Scarf","Price":"12.00","Category":"Apparel"
},{
"ID":4,"Name":"Yo-yo","Price":"4.95","Category":"Toys"
},{
"ID":5,"Name":"Puzzle","Price":"8.00","Category":"Toys"
}
]
}
Entidade
Para obter um produto individual, envie uma solicitação GET para http://localhost:port/odata/Products(1)
, em que "1" é a ID do produto.
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
DataServiceVersion: 3.0
Date: Mon, 23 Sep 2013 23:04:29 GMT
Content-Length: 140
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element","ID":1,
"Name":"Hat","Price":"15.00","Category":"Apparel"
}
Formatos de serialização OData
O OData dá suporte a vários formatos de serialização:
- Atom Pub (XML)
- JSON "light" (introduzido no OData v3)
- JSON "detalhado" (OData v2)
Por padrão, a API Web usa o formato "light" AtomPubJSON.
Para obter o formato AtomPub, defina o cabeçalho Accept como "application/atom+xml". Aqui está um exemplo de corpo de resposta:
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="http://localhost:60868/odata" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
<id>http://localhost:60868/odata/Products(1)</id>
<category term="ProductService.Models.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="http://localhost:60868/odata/Products(1)" />
<link rel="self" href="http://localhost:60868/odata/Products(1)" />
<title />
<updated>2013-09-23T23:42:11Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:ID m:type="Edm.Int32">1</d:ID>
<d:Name>Hat</d:Name>
<d:Price m:type="Edm.Decimal">15.00</d:Price>
<d:Category>Apparel</d:Category>
</m:properties>
</content>
</entry>
Você pode ver uma desvantagem óbvia do formato Atom: ele é muito mais detalhado do que a luz JSON. No entanto, se você tiver um cliente que entenda o AtomPub, o cliente poderá preferir esse formato em vez de JSON.
Aqui está a versão de luz JSON da mesma entidade:
{
"odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element",
"ID":1,
"Name":"Hat",
"Price":"15.00",
"Category":"Apparel"
}
O formato de luz JSON foi introduzido na versão 3 do protocolo OData. Para compatibilidade com versões anteriores, um cliente pode solicitar o formato JSON "detalhado" mais antigo. Para solicitar JSON detalhado, defina o cabeçalho Accept como application/json;odata=verbose
. Esta é a versão detalhada:
{
"d":{
"__metadata":{
"id":"http://localhost:18285/odata/Products(1)",
"uri":"http://localhost:18285/odata/Products(1)",
"type":"ProductService.Models.Product"
},"ID":1,"Name":"Hat","Price":"15.00","Category":"Apparel"
}
}
Esse formato transmite mais metadados no corpo da resposta, o que pode adicionar uma sobrecarga considerável em uma sessão inteira. Além disso, ele adiciona um nível de indireção encapsulando o objeto em uma propriedade chamada "d".