Создание конечной точки OData v3 с использованием Web API 2

Майк Уосон

Скачивание завершенного проекта

Open Data Protocol (OData) — это протокол доступа к данным для Интернета. OData предоставляет универсальный способ структурирования данных, запроса данных и управления набором данных с помощью операций CRUD (создание, чтение, обновление и удаление). OData поддерживает форматы AtomPub (XML) и JSON. OData также определяет способ предоставления метаданных о данных. Клиенты могут использовать метаданные для обнаружения сведений о типах и связях для набора данных.

ASP.NET веб-API упрощает создание конечной точки OData для набора данных. Вы можете точно контролировать, какие операции OData поддерживаются конечной точкой. Можно разместить несколько конечных точек OData, а также конечных точек, отличных от OData. У вас есть полный контроль над моделью данных, внутренней бизнес-логикой и уровнем данных.

Версии программного обеспечения, используемые в руководстве

Поддержка веб-API OData была добавлена в обновление ASP.NET и Web Tools 2012.2. Однако в этом руководстве используется каркас, добавленный в Visual Studio 2013.

В этом руководстве вы создадите простую конечную точку OData, которую клиенты могут запрашивать. Вы также создадите клиент C# для конечной точки. После завершения работы с этим руководством в следующем наборе руководств показано, как добавить дополнительные функциональные возможности, включая отношения сущностей, действия и $expand/$select.

Создание проекта Visual Studio

В этом руководстве вы создадите конечную точку OData, которая поддерживает основные операции CRUD. Конечная точка предоставляет один ресурс, список продуктов. В последующих руководствах будут добавлены дополнительные возможности.

Запустите Visual Studio и выберите новый проект на начальной странице. Или в меню "Файл " выберите "Создать " и " Проект".

В области "Шаблоны " выберите "Установленные шаблоны " и разверните узел Visual C#. В разделе Visual C# выберите "Веб". Выберите шаблон веб-приложения ASP.NET .

Снимок экрана нового окна проекта, показывающий путь к области шаблона и выделенные указания для выбора опции

В диалоговом окне "Создать ASP.NET проект " выберите пустой шаблон. В разделе "Добавление папок и основных ссылок на..." проверьте веб-API. Нажмите кнопку ОК.

Снимок экрана: диалоговое окно проекта S P dot NET, отображающее поля параметров шаблона и выделение пустого параметра.

Добавление модели сущностей

Модель — это объект, представляющий данные в приложении. Для работы с этим руководством нам нужна модель, представляющая продукт. Модель соответствует типу сущности OData.

В обозревателе решений щелкните правой кнопкой мыши папку Models. В контекстном меню выберите "Добавить ", а затем выберите "Класс".

Снимок экрана с диалоговым окном обозревателя решений, в котором отображается меню для каждой выделенной опции, плавно переходя к опции класса.

В диалоговом окне "Добавление нового элемента" назовите класс Product.

Снимок экрана: окно

Замечание

По соглашению классы моделей помещаются в папку Models. Вам не нужно следовать этому соглашению в собственных проектах, но мы будем использовать его для этого руководства.

В файле Product.cs добавьте следующее определение класса:

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Category { get; set; }
}

Свойство ID будет ключом сущности. Клиенты могут запрашивать продукты по идентификатору. Это поле также будет первичным ключом в серверной базе данных.

Теперь создайте проект. На следующем шаге мы будем использовать некоторые шаблоны Visual Studio, которые используют отражение для поиска типа продукта.

Добавление контроллера OData

Контроллер — это класс, который обрабатывает HTTP-запросы. Вы определяете отдельный контроллер для каждого набора сущностей в службе OData. В этом руководстве мы создадим один контроллер.

В обозревателе решений щелкните правой кнопкой мыши папку Контроллеров. Нажмите кнопку "Добавить " и выберите "Контроллер".

Снимок экрана: окно обозревателя решений, в котором выделен параметр контроллера, который затем отображает меню для добавления контроллера данных O.

В диалоговом окне "Добавить шаблон" выберите "Контроллер OData для Web API 2 с действиями, использующий Entity Framework".

Снимок экрана: экран

В диалоговом окне добавления контроллера назовите контроллер ProductsController. Установите флажок "Использовать асинхронные действия контроллера". В раскрывающемся списке "Модель" выберите класс Product.

Снимок экрана: диалоговое окно добавления контроллера, отображающее поля для имени контроллера, раскрывающегося списка класса модели и класса контекста данных.

Нажмите кнопку "Создать контекст данных"... Оставьте имя по умолчанию для типа контекста данных и нажмите кнопку "Добавить".

Снимок экрана: новое окно контекста данных, отображающее поле для нового типа контекста данных и отображающее имя по умолчанию для типа контекста данных.

Нажмите кнопку "Добавить" в диалоговом окне "Добавить контроллер", чтобы добавить контроллер.

Снимок экрана: диалоговое окно добавления контроллера с различными требованиями к полю с флажком

Примечание. Если появится сообщение об ошибке с сообщением "Возникла ошибка при получении типа...", убедитесь, что вы создали проект Visual Studio после добавления класса Product. Каркас использует отражение для поиска класса.

Снимок экрана: Microsoft Visual Studio, отображающий красный круг

Шаблон добавляет в проект два файла кода:

  • Products.cs определяет контроллер веб-API, реализующий конечную точку OData.
  • ProductServiceContext.cs предоставляет методы для запроса базовой базы данных с помощью Entity Framework.

Снимок экрана окна проекта, на котором показано меню сервиса продуктов и выделены два недавно добавленных файла под контроллерами и моделями.

Добавить EDM и маршрут

В обозревателе решений разверните папку App_Start и откройте файл с именем WebApiConfig.cs. Этот класс содержит код конфигурации для веб-API. Замените этот код следующим образом:

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());
        }
    }
}

Этот код выполняет следующие две операции:

  • Создает модель данных сущностей (EDM) для OData-эндпоинта.
  • Добавляет маршрут для конечной точки.

EDM — это абстрактная модель данных. EDM используется для создания документа метаданных и определения URI для службы. ODataConventionModelBuilder создает EDM с помощью набора соглашений об именовании по умолчанию EDM. Для этого подхода требуется наименьший код. Если требуется больше контроля над EDM, можно использовать класс ODataModelBuilder для создания EDM, добавив свойства, ключи и свойства навигации явным образом.

Метод EntitySet добавляет набор сущностей в EDM:

modelBuilder.EntitySet<Product>("Products");

Строка "Products" определяет имя набора сущностей. Имя контроллера должно соответствовать имени набора сущностей. В этом руководстве набор сущностей называется Products, а контроллер называется ProductsController. Если вы назвали набор сущностей ProductSet, вы назовете контроллер ProductSetController. Обратите внимание, что конечная точка может иметь несколько наборов сущностей. Вызов EntitySet<T> для каждого набора сущностей, а затем определите соответствующий контроллер.

Метод MapODataRoute добавляет маршрут для конечной точки OData.

config.Routes.MapODataRoute("ODataRoute", "odata", model);

Первый параметр — понятное имя маршрута. Клиенты вашей службы не видят этого имени. Второй параметр — это префикс URI для конечной точки. Учитывая этот код, URI для набора сущностей Products — http://hostname/odata/Products. Приложение может иметь несколько конечных точек OData. Для каждой конечной точки вызовите MapODataRoute и укажите уникальное имя маршрута и уникальный префикс URI.

Инициализация базы данных (необязательно)

На этом шаге вы будете использовать Entity Framework для заполнения базы данных с некоторыми тестовых данных. Этот шаг является необязательным, но он позволяет сразу протестировать конечную точку OData.

В меню "Сервис" выберите диспетчер пакетов NuGet, а затем консоль диспетчера пакетов. В окне консоли диспетчера пакетов введите следующую команду:

Enable-Migrations

При этом добавляется папка с именем Migrations и файл кода с именем Configuration.cs.

Снимок экрана: меню службы продуктов в обозревателе решений, обозначающее только что добавленную папку, названную migrations, и показывающую файл в ней.

Откройте этот файл и добавьте следующий код в Configuration.Seed метод.

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" },
    });
}

В окне консоли диспетчера пакетов введите следующие команды:

Add-Migration Initial
Update-Database

Эти команды создают код, который создает базу данных, а затем выполняет этот код.

Изучение конечной точки OData

В этом разделе мы будем использовать прокси-сервер веб-отладки Fiddler для отправки запросов в конечную точку и проверки сообщений ответа. Это поможет вам понять возможности конечной точки OData.

В Visual Studio нажмите клавишу F5, чтобы начать отладку. По умолчанию Visual Studio открывает браузер http://localhost:*port*, где порт — это номер порта, который настроен в параметрах проекта.

Номер порта можно изменить в параметрах проекта. В обозревателе решений щелкните проект правой кнопкой мыши и выберите пункт "Свойства". В окне свойств выберите "Интернет". Введите номер порта в поле URL-адрес проекта.

Документ службы

Документ службы содержит список наборов сущностей для конечной точки OData. Чтобы получить документ службы, отправьте запрос GET в корневой универсальный код ресурса (URI) службы.

С помощью Fiddler введите следующий URI на вкладке Composer: http://localhost:port/odata/, где port — это номер порта.

Снимок экрана: окно документа службы, отображающее различные вкладки с выбранной вкладкой

Нажмите кнопку "Выполнить ". Fiddler отправляет HTTP-запрос GET в приложение. Вы увидите ответ в списке веб-сеансов. Если все работает, код состояния будет 200.

Снимок экрана: список веб-сеансов, показывающий протокол H T T P с результатом 200 и адресом U R L и узлом.

Дважды щелкните ответ в списке веб-сеансов, чтобы просмотреть сведения об ответном сообщении на вкладке "Инспекторы".

Снимок экрана: вкладка инспектора веб-сеансов, где отображаются ответы заголовков запросов и информация об X M L.

Необработанное сообщение HTTP-ответа должно выглядеть примерно так:

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>

По умолчанию веб-API возвращает документ службы в формате AtomPub. Чтобы запросить JSON, добавьте следующий заголовок в HTTP-запрос:

Accept: application/json

Снимок экрана окна веб-сеансов, показывающий ответ от API в разделе «Заголовки запросов», и обведено место для записи запроса JSON.

Теперь http-ответ содержит полезные данные 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"
    }
  ]
}

Документ метаданных службы

В документе метаданных службы описывается модель данных службы с помощью XML-языка, называемого языком определения концептуальной схемы (CSDL). Документ метаданных показывает структуру данных в службе и может использоваться для создания клиентского кода.

Чтобы получить документ метаданных, отправьте запрос http://localhost:port/odata/$metadataGET. Ниже приведены метаданные конечной точки, показанной в этом руководстве.

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>

Набор сущностей

Чтобы получить набор сущностей продуктов, отправьте GET запрос на 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"
    }
  ]
}

Объект

Чтобы получить отдельный продукт, отправьте запрос http://localhost:port/odata/Products(1)GET, где "1" является идентификатором продукта.

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"
}

Форматы сериализации OData

OData поддерживает несколько форматов сериализации:

  • Atom Pub (XML)
  • JSON "light" (представлено в OData версии 3)
  • JSON "подробный" (OData версии 2)

По умолчанию веб-API использует формат AtomPubJSON light.

Чтобы получить формат AtomPub, задайте для заголовка Accept значение application/atom+xml. Ниже приведен пример текста ответа:

<?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>

Вы можете заметить один очевидный недостаток формата Atom: он гораздо более многословен, чем JSON light. Однако если у вас есть клиент, который понимает AtomPub, клиент может предпочесть этот формат в формате JSON.

Представлена облегченная версия JSON той же сущности:

{
  "odata.metadata":"http://localhost:60868/odata/$metadata#Products/@Element",
  "ID":1,
  "Name":"Hat",
  "Price":"15.00",
  "Category":"Apparel"
}

Светлый формат JSON появился в версии 3 протокола OData. Для обратной совместимости клиент может запросить устаревший "verbose"-формат JSON. Чтобы запросить подробный JSON, задайте заголовок Accept в application/json;odata=verbose. Ниже приведена подробная версия:

{
  "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"
  }
}

Этот формат передает дополнительные метаданные в тексте отклика, что может добавить значительную нагрузку на весь сеанс. Кроме того, он добавляет уровень косвенности, упаковывая объект в свойство с именем "d".

Дальнейшие шаги