Нашли ошибку или опечатку? Выделите текст и нажмите

Поменять цветовую

гамму сайта?

Поменять
Обновления сайта
и новые разделы

Рекомендовать в Google +1

Использование Web API

115
1

Средство Web API основано на добавлении в приложение ASP.NET MVC Framework контроллера специального вида. Эта разновидность контроллеров, которая называется контроллером API, обладает двумя характеристиками:

  • Методы действий возвращают объекты моделей, а не объекты типа ActionResult.

  • Методы действий выбираются на основе HTTP-метода, используемого в запросе.

Объекты моделей, возвращаемые методом действия контроллера API, кодируются в формате JSON и отправляются клиенту. Контроллеры API предназначены для доставки веб-служб данных, поэтому они не поддерживают представления, компоновки или любые другие средства, которые применялись для генерации HTML-разметки в примере созданного приложения.

Отсутствие возможности у контроллера API генерировать HTML-разметку из представлений является причиной, по которой в одностраничных приложениях комбинируются стандартные приемы ASP.NET MVC Framework с Web API. Инфраструктура ASP.NET MVC Framework выполняет шаги, требуемые для доставки HTML-содержимого пользователю (включая аутентификацию, авторизацию, выбор и визуализацию представления). После того, как HTML-содержимое доставлено в браузер, запросы Ajax, генерируемые содержащимся внутри кодом JavaScript, будут обрабатываться контроллером Web API.

Как демонстрировалось ранее, в обычных контроллерах можно создавать методы действий, которые возвращают данные JSON для поддержки Ajax, но контроллер API предлагает альтернативный подход. Этот подход предусматривает отделение действий, относящихся к данным, от действий, связанных с представлением, и делает создание универсальных приложений Web API быстрым и простым.

Создание контроллера Web API

Добавление средства Web API к приложению осуществляется исключительно просто. Частично это объясняется тем, что создается элементарная веб-служба, но также и интеграцией с лежащей в основе инфраструктурой ASP.NET MVC Framework. В папке Controllers проекта создается файл класса по имени WebController.cs, в котором определяется контроллер, как показано в примере ниже:

using System;
using System.Collections.Generic;
using System.Web.Http;
using WebServices.Models;

namespace WebServices.Controllers
{
    public class WebController : ApiController
    {
        private ReservationRepository repo = ReservationRepository.Current;

        public IEnumerable<Reservation> GetAllReservations()
        {
            return repo.GetAll();
        }

        public Reservation GetReservation(int id)
        {
            return repo.Get(id);
        }

        public Reservation PostReservation(Reservation item)
        {
            return repo.Add(item);
        }

        public bool PutReservation(Reservation item)
        {
            return repo.Update(item);
        }

        public void DeleteReservation(int id)
        {
            repo.Remove(id);
        }
    }
}

Это и все, что требуется для создания контроллера API. Контроллер API имеет пять методов действий, которые отображаются на функциональные возможности хранилища и предоставляют доступ через веб-службу к объектам Reservation.

Тестирование контроллера API

Вскоре будут даны пояснения, как работает контроллер API, но сначала необходимо выполнить простой тест. Запустите приложение. После того, как браузер загрузит корневой URL проекта, перейдите на URL вида /api/web. Результат, который будет получен, зависит от применяемого браузера. Если вы используете Internet Explorer, то получите предложение сохранить или открыть файл, содержащий данные JSON.

Если переход на упомянутый URL осуществлялся с помощью другого браузера, такого как Google Chrome, то браузер отобразит приведенные ниже XML-данные:

<ArrayOfReservation xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns="http://schemas.datacontract.org/2004/07/WebServices.Models">
	<Reservation>
		<ClientName>Петр</ClientName>
		<Location>Отель</Location>
		<ReservationId>1</ReservationId>
	</Reservation>
	<Reservation>
		<ClientName>Вася</ClientName>
		<Location>Библиотека</Location>
		<ReservationId>2</ReservationId>
	</Reservation>
	<Reservation>
		<ClientName>Игорь</ClientName>
		<Location>Столовая</Location>
		<ReservationId>3</ReservationId>
	</Reservation>
</ArrayOfReservation>

Здесь интересно отметить пару моментов. Первый заключается в том, что запрос URL вида /api/web производит список всех объектов модели вместе с их свойствами, из которого можно сделать вывод, что был вызван метод действия GetAllReservations() контроллера Reservation.

Второй момент касается того, что разные браузеры получают разные форматы данных. Если вы попробуете сделать это самостоятельно, то можете получить другие результаты, т.к. в более поздних версиях браузеров может измениться способ выдачи запросов, однако вы заметите, что одни результаты имеют формат JSON, а другие - формат XML. (Также должно стать понятно, почему в веб-службах формат JSON почти повсеместно заменил XML. Формат XML более многословен и труден в обработке, особенно в случае использования JavaScript.)

Разные форматы данных применяются из-за того, что Web API использует HTTP-заголовок Accept в запросе для определения, с каким типом данных клиент предпочитает работать. Браузер Internet Explorer получает данные JSON, поскольку отправляет следующий заголовок Accept:

Accept: text/html, application/xhtml+xml, */*

Браузер указывает, что в первую очередь предпочитает содержимое text/html, а затем application/xhtml+xml. Последняя часть заголовка Accept выглядит как */* и означает, что браузер будет принимать любой тип данных, если первые два оказываются недоступными.

Средство Web API поддерживает форматы JSON и XML, но отдает предпочтение формату JSON, который и будет использоваться в ответ на часть */* заголовка Accept для Internet Explorer. А вот заголовок Accept, отправляемый браузером Google Chrome:

Accept:text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8

Важная часть заголовка выделена полужирным: браузер Google Chrome указывает, что предпочитает получать данные application/xml перед */*. Контроллер Web API учитывает это предпочтение и доставляет данные в формате XML. Об этом упоминается потому, что общей проблемой, связанной с Web API, является получение данных в нежелательном формате. Подобное происходит из-за того, что заголовок Accept выдает неожиданное предпочтение относительно формата или вообще отсутствует в запросе.

Работа контроллера API

Чтобы получить намного больше сведений о работе контроллера API, необходимо перейти на URL вида /api/web/3. Вы увидите следующие данные в формате XML (или в формате JSON, если применяется другой браузер):

<Reservation>
	<ClientName>Игорь</ClientName>
	<Location>Столовая</Location>
	<ReservationId>3</ReservationId>
</Reservation>

На этот раз контроллер API возвратил детали объекта Reservation, значение свойства ReservationId которого соответствует последнему сегменту запрошенного URL. Формат и сегменты URL были описаны в статье Шаблоны URL, где объяснялась система маршрутизации ASP.NET MVC Framework.

Контроллеры API имеют собственную конфигурацию маршрутизации, полностью отделенную от остальных частей приложения. Для новых проектов Visual Studio создает стандартную конфигурацию в файле /App_Start/WebApiConfig.cs, содержимое которого приведено в примере ниже. Это один из файлов, которые Visual Studio добавляет в проект, если при его создании был отмечен флажок Web API.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace WebServices
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Файл WebApiConfig.cs содержит маршруты, используемые контроллерами API, и применяет другие классы, отличающиеся от обычных маршрутов MVC, которые определены в файле RouteConfig.cs. Средство Web API реализовано как автономная функциональная возможность ASP.NET и может применяться за пределами инфраструктуры ASP.NET MVC Framework. Это означает, что в Microsoft продублировали ряд ключевых функций ASP.NET MVC Framework в пространстве имен System.Web.Http, чтобы обеспечить раздельное существование средств MVC и Web API. (При написании приложения ASP.NET MVC Framework такое дублирование выглядит странным, однако оно имеет смысл, т.к. в Microsoft пытаются ориентировать на использование средства Web API также и разработчиков, не применяющих шаблон MVC.)

Среда Visual Studio также помещает в метод Application_Start() класса Global.asax вызов Configure(), который добавит маршруты Web API к конфигурации приложения:

using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Http;

namespace WebServices
{
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            RouteConfig.RegisterRoutes(RouteTable.Routes);            
        }
    }
}

В результате приложение имеет два набора маршрутов: те, что используются для контроллеров ASP.NET MVC Framework, и те, что применяются для контроллеров Web API.

Выбор действия контроллером API

Стандартный маршрут Web API, который был показан в примере выше, имеет статический сегмент api, а также переменные сегментов controller и id, являющиеся необязательными. Ключевое отличие от обычного маршрута MVC состоит в том, что переменная сегмента action отсутствует - именно здесь формируется поведение контроллеров API.

Когда в приложение поступает запрос, соответствующий маршруту Web API, действие определяется на основе метода HTTP, используемого для выдачи запроса. При тестировании контроллера API путем запроса в браузере URL вида /api/reservation браузер укажет HTTP-метод GET.

Класс ApiController, который является базовым для контроллеров Web API, выясняет необходимый контроллер на основе маршрута и применяет метод HTTP для поиска подходящих методов действий.

Соглашение по именованию методов действий контроллера API предусматривает снабжение имени метода действия префиксом в виде поддерживаемого им HTTP-метода и включение ссылки на тип модели, с которым метод действия работает. Однако это просто соглашение, т.к. Web API будет обеспечивать соответствие любому методу действия, имя которого содержит метод HTTP, используемый для выполнения запроса.

В рассматриваемом примере запрос GET приведет в результате к выбору между GetAllReservations() и GetReservation(), но также бы подошли имена методов наподобие DoGetReservation() или даже ThisIsTheGetAction().

Чтобы принять решение, какой из этих двух методов действий выбрать, контроллер просматривает аргументы, которые они принимают, и с помощью переменных маршрутизации находит наилучшее соответствие. В случае запроса URL вида /api/reservation нет никаких переменных маршрутизации за исключением controller, поэтому выбирается метод GetAllReservations(), т.к. он не принимает аргументов.

При запросе URL вида /api/reservation/3 предоставляется значение для необязательной переменной сегмента id, что приведет к выбору метода GetReservation(), потому что он принимает аргумент id. Остальные действия в контроллере API ориентированы на другие HTTP-методы: POST, DELETE и PUT. Это основа для стиля REST (Representation State Transfer - передача состояния представления) средства Web API, чаще называемого службой, поддерживающей REST, когда операция указывается путем комбинирования URL и метода HTTP, используемого для ее запрашивания.

REST - это стиль API-интерфейса, а не хорошо определенная спецификация, поэтому существуют разногласия относительно того, какие в точности признаки делают веб-службу поддерживающей REST. Один из предметов спора связан с тем, что сторонники чистоты определений не считают веб-службы, которые возвращают данные в формате JSON, поддерживающими REST. Как и при любых разногласиях, касающихся архитектурного шаблона, претензии зачастую произвольны и неконкретны. Я стараюсь быть прагматичным в отношении применяемых шаблонов, поэтому считаю службы JSON поддерживающими REST.

Отображение методов HTTP на методы действий

Ранее было указано, что базовый класс ApiController использует метод HTTP для выяснения, на какие методы действий направлять запросы. Это хороший подход, но он означает применение неестественных имен методов, которые не соответствуют соглашениям об именовании, используемым в остальных частях приложения. Например, метод PutReservation() мог бы иметь более естественное имя UpdateReservation(). Мало того, что имя UpdateReservation() делает очевидным назначение этого метода (обновить заявку на бронирование), но оно может также обеспечить более прямое отображение между действиями в контроллере и методами в хранилище.

У вас может возникнуть соблазн унаследовать класс хранилища от ApiController и открыть методы хранилища напрямую как Web API. Настоятельно рекомендуется не поступать так, а создавать отдельный контроллер, даже если он настолько же прост, как в рассматриваемом примере. В какой-то момент методы, которые вы хотите предоставлять через Web API, станут идти вразрез с возможностями хранилища, и наличие отдельного класса контроллера API упростит управление этим.

Пространство имен System.Web.Http содержит набор атрибутов, которые можно использовать, чтобы указать, для каких методов HTTP должно применяться то или иное действие. В примере ниже демонстрируется применение двух таких атрибутов для получения более естественного набора имен методов:

using System;
using System.Collections.Generic;
using System.Web.Http;
using WebServices.Models;

namespace WebServices.Controllers
{
    public class WebController : ApiController
    {
        private ReservationRepository repo = ReservationRepository.Current;

        public IEnumerable<Reservation> GetAllReservations()
        {
            return repo.GetAll();
        }

        public Reservation GetReservation(int id)
        {
            return repo.Get(id);
        }

        [HttpPost]
        public Reservation CreateReservation(Reservation item)
        {
            return repo.Add(item);
        }

        [HttpPut]
        public bool UpdateReservation(Reservation item)
        {
            return repo.Update(item);
        }

        public void DeleteReservation(int id)
        {
            repo.Remove(id);
        }
    }
}

Здесь можно заметить дублирование средств ASP.NET MVC Framework и Web API. Атрибуты HttpPost и HttpPut, использованные в примере, имеют то же самое назначение, что и аналогично именованные атрибуты в MVC, но только они определены в пространстве имен System.Web.Http, а не System.Web.Mvc. Помимо дублирования имен, эти атрибуты функционируют точно таким же образом, и в конечном итоге получаются более удобные имена для методов, которые по-прежнему будут работать с HTTP-методами POST и PUT. (Разумеется, атрибуты предусмотрены для всех методов HTTP, включая GET, DELETE и т.д.)

Пройди тесты