REST API для взаимодействия сервисов


Оглавление (нажмите, чтобы открыть):

SAP@Pitroff.Ru

Disclaimer:
1) Часть 1 — теоретическая, может быть скучно :);
2) если вы до этого сталкивались и работали с протоколом HTTP (знаете, как устроен, формат запроса/ответа) — проблем с пониманием REST у вас возникнуть не должно. Если не уверены — рекомендую сначала почитать
про HTTP на Википедии.

Из википедии: REST (Representational State Transfer) — архитектурный стиль взаимодействия компонентов распределённого приложения в сети.

Для веб-сервисов или API, построенных с учётом REST (то есть не нарушающих накладываемых им ограничений), применяют термин «RESTful».

Термин REST был введен в 2000 году Роем Филдингом, одним из авторов HTTP-протокола.

REST — достаточно распространенный в интернете способ взаимодействия клиентских приложений и сервисов. Сервис, написанный с учетом ограничений и правил REST принято называть RESTful.

Очень важно следующее: REST — это НЕ протокол или стандарт.
В отличие от веб-сервисов на основе SOAP, не существует утвержденного или принятого официально стандарта для RESTful сервисов. REST является архитектурой, в то время как SOAP является протоколом.

То есть REST — это набор принципов и ограничений взаимодействия клиента и сервера в сети интернет, использующий существующие стандарты (HTTP протокол, стандарт построения URL, форматы данных JSON и XML) в ходе взаимодействия.

Правила и ограничения REST-архитектуры

В REST есть шесть основных ограничений:

1) Модель взаимодействия клиент-сервер.

Если говорить простыми словами, то нужно разделить требования и желания клиента/пользователя от возможностей и логики работы сервиса.

Пример: REST -сервис предоставляет информацию по продаваемому автомобилю из информационной базы.
По архитектуре REST нельзя закладывать в код сервиса какой-либо выбор по умолчанию или ограничение за пользователя (цвет, марку, и т.п.) — все должно быть предусмотрено в виде входящих параметров сервиса. Выбор (в том числе, значения по умолчанию, фильтрация) — это дело программы-клиента (страницы, на которой пользователь заполняет фильтры или кликает по предложенным вариантам).

Нужно строить логику работы своего приложения из расчета на любого пользователя, предоставив максимально возможный выбор параметров «на откуп» клиентскому приложению.

2) Отсутствие состояния.

Согласно REST , сервис не хранит результаты предыдущей операции. То есть работаем по принципу «спросил-ответил-забыл». ��

3) Кэширование.

Речь о том, что пара «запрос-ответ» может быть помечена как кэшируемая и храниться на стороне пользователя. Можно провести аналогию с кэшированием веб-страниц — скачав однажды, ее можно просматривать, не обращаясь заново к серверу.
Однако, если запрос помечен как «не кэшируемый» — то при каждом новом запросе клиент должен вызывать сервер.

4) Единообразие интерфейса.

REST -архитектура определяет, что любой REST -сервис должен быть понятен без его разработчика. Для этого REST вводит четыре правила:

Чтобы отделить один ресурс(сервис) от другого используются запросы/ссылки (URI).

Пример: информация по ISBN-коду книги в библиотеке — http://library.net/books/info/
продление книги по ISBN — http://library.net/books/prolong/

Пример неподходящего разделения: http://library/books/operation, где операция и код книги должны быть в теле запроса.

  • Манипуляция ресурсами через представление.

По простому: если мы получили мета-данные по объекту (из предыдущего примера — код ISBN книги) — то можем сделать с этим объектом любую операцию из состава API.

Каждое сообщение содержит достаточно информации, чтобы понять каким образом его обрабатывать. В это входит как техническая обработка (например, возврат в HTTP-заголовке типа содержимого ответа), так и смысловая часть сообщения.

  • Гипермедиа как способ определить состояние приложения.

Гипермедиа — это расширение термина гипертекст. Если упростить — то это все интерактивные объекты сети: рисунки, схемы, видео (пример — youtube), текст и ссылки.

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

5) Слои.

Клиент не должен знать, работает ли он с конечной точкой, или с цепочкой/иерархией серверов. То есть REST предусматривает возможность кластерной/распределенной работы серверной части, но при этом для клиента сервис должен оставаться цельным и не требовать дополнительных данных/действий от клиента.

6) Код по требованию (необязательное ограничение).

Тут разработчик архитектуры REST предложил следующее — по запросу, сервис должен отдавать исполняемый код в виде апплета или сценария, для выполнения на стороне клиента. На практике — практически не используется.

RESTful сервис

С точки зрения пользователя, RESTful сервисы — это ресурсы сети, которыми может быть всё, что можно поименовать и представить.

Сервисы описывают себя сами. Ну или пытаются :), но чем логичней и понятней система наименования (url) — тем удобнее работать с API.
Поэтому api/books/get/ISBN/2-266-11156 лучше и понятней, с точки зрения клиента, чем /api?obj=books&func=get&search=ISBN&number=2-266-11156

В ресурсах содержатся ссылки на другие ресурсы (гиперсреда). То есть, получив первичную информацию по объекту, в составе ответа может быть ссылка на ресурс, дающий полную информацию по этому объекту.

Пример: Используя REST API находим книгу по url api/books/get/ISBN/2-266-11156. В ответе сервис высылает блок информации, в нем есть элемент infolink, содержащий ссылку api/books/info/2-266-11156.

Взаимодействие с ресурсами осуществляется с помощью вызова URL ресурса и стандартных команд HTTP (GET, POST, PUT и DELETE). Эти операции обычно соответствуют операциям с объектом, например, так:

Create PUT (с пустым ID объекта)
POST
Read GET
Update PUT (с существующим ID объекта)
Delete DELETE

Ответ сервис также отправляет в виде кода возврата HTTP, например:

200 Ок
404 Не найден
400 Плохой запрос (Bad Request)
500 Внутренняя ошибка сервера

На деле кодов возврата у HTTP существенно больше (wiki — HTTP codes). Также дополнительные данные об ошибке могут передаваться в теле ответа.

Формат данных, передаваемых между клиентом и сервисом указывается с помощью заголовка HTTP — параметр типа содержимого Content-Type. Обычно это JSON или XML, но могут быть разнообразными (например, JPG, PNG — для сервиса обработки графики).

JSON vs XML

Для специалистов, работающих с SAP PI/PO, XML — это практически второй родной язык. �� JSON же в мире SAP практически не используется, поэтому уделю немного внимания этой теме.

Сходство JSON и XML:
Это понятные языки — их можно читать без специального ПО и понимать содержимое;
Это иерархичные языки — данные организованы при помощи различных уровней вложенности;
Для получения данных/значений XML/JSON нужно разобрать — подвергнуть «парсингу»,»распарсить».

Основные различия:
JSON компактнее (данные в формате JSON имеют меньший размер, чем в XML);
JSON быстрее в чтении/передаче данных;
JSON позволяет вариативность типов/структур (например, в JSON-схеме разрешены элементы, имеющие один из нескольких типов — в схеме данных можно определить, что элемент price может быть текстом, а может быть структурой из двух элементов). После привычки к строгости XML вариативность JSON сложно понять и принять. ��

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

На данный момент практически каждый крупный интернет-ресурс имеет свой REST API:

Поэтому хорошему интегратору надо уметь с ними работать. Разработчики SAP это учли — и выпустили адаптер к SAP Process Integration для поддержки REST .

А вот как с ним работать — будет в следующей серии. ��

Что такое такое rest api?

Что такое такое rest api ? Объясните, пожалуйста, понятными словами. Википедия, увы, не смогла ответить.

Специалист по rest api должен разбираться в, например, api vk ? Или это разные вообше вещи? В чем разница?

  • Вопрос задан более трёх лет назад
  • 105944 просмотра

API социальных сетей — это вполне типичные примеры реализации REST API.

REST (RESTful) — это общие принципы организации взаимодействия приложения/сайта с сервером посредством протокола HTTP. Особенность REST в том, что сервер не запоминает состояние пользователя между запросами — в каждом запросе передаётся информация, идентифицирующая пользователя (например, token, полученный через OAuth-авторизацию) и все параметры, необходимые для выполнения операции.

Всё взаимодействие с сервером сводится к 4 операциям (4 — это необходимый и достаточный минимум, в конкретной реализации типов операций может быть больше):
1. получение данных с сервера (обычно в формате JSON, или XML)
2. добавление новых данных на сервер
3. модификация существующих данных на сервере
4. удаление данных на сервере

Операция получения данных не может приводить к изменению состояния сервера.

Для каждого типа операции используется свой метод HTTP-запроса:
1. получение — GET
2. добавление — POST
3. модификация — PUT
4. удаление — DELETE

GET-запрос /rest/users — получение информации о всех пользователях
GET-запрос /rest/users/125 — получение информации о пользователе с > POST-запрос /rest/users — добавление нового пользователя
PUT-запрос /rest/users/125 — изменение информации о пользователе с > DELETE-запрос /rest/users/125 — удаление пользователя с >

zigins >Заметки Android

17 августа 2020

Что такое REST?

REST — (сокр. от англ. Representational State Transfer — «передача состояния представления») архитектурный стиль взаимодействия компонентов распределённого приложения в сети по модели клиент-сервер. Был разработан в диссертации Роя Филдинга в 200 году, как альтернатива SOAP, когда запрос клиента несет в себе исчерпывающую информацио о желаемом ответе сервера и сервер не обязан сохранять сессию взаимодействия с клиентом.

Особенности архитектурного стиля:

  • Каждая сущность должна иметь уникальный идентификатор – URI.
  • Сущности должны быть связаны между собой.
  • Для чтения и изменения данных должны использоваться стандартные методы.
  • Должна быть поддержка нескольких типов ресурсов.
  • Взаимодействие должно осуществляться без состояния.

Стандартные методы таковы:

  • GET – получение данных без их изменения. Это наиболее популярный и легкий метод. Он только возвращает данные, а не изменяет их, поэтому на клиенте вам не нужно заботиться о том, что вы можете повредить данные.
  • POST – метод, подразумевающий вставку новых записей.
  • PUT – метод, подразумевающий изменение существующих записей.
  • PATCH – метод, подразумевающий изменение идентификатора существующих записей.
  • DELETE – метод, подразумевающий удаление записей.


Что такое REST-API?

REST API – это набор удаленных вызовов стандартных методов, возвращающих данные в определенном формате.

Где используется стиль REST в Android?

1) См. реализации различных библиотек REST-клиентов, например: Retrofit, Volley, RoboSpice… См. статью Volley vs Retrofit

Задачи, которые требуется решить при реализации REST-клиента

Перечислим, какие основные задачи придется решать при реализации REST-клиента на Android согласно паттернам Virgil Dobjanschi (Это так называемые паттерны A/B/C, которые описывают способы реализации REST-клиента под Android. Подробней о паттернах на русском можно глянуть здесь (лекция 2) или здесь):

  • Управление сервисом: запуск, остановка.
  • Передача результатов из сервиса в активити.
  • Кэшировать результатов в sqlite.
  • Фиксирование статуса данных sqlite перед и после выполнения REST-запроса.
  • Запись информации о проводимых REST-операциях в sqlite.
  • Парсинг полученных данных.
  • Конструирование REST-запроса на основе URI и набора параметров.
  • Выполнение сетевых запросов к REST-серверу.
  • Чистка базы данных от устаревших данных.
  • В случае неудачи REST-запроса, пытаться повторить запрос (например, экспоненциально увеличивая время между запросами).
  • Возможность отложенного запуска REST-запроса через SyncAdapter.

Некоторые важные моменты для реализации REST-клиента под Android, согласно паттернам A/B/C

  • Данные, полученные от REST-сервера, всегда сохраняются в sqlite. Напрямую в Activity они никогда не передаются. Вместо этого в Activity передается уведомление о том, что данные загружены в sqlite и их можно оттуда загрузить (вариант — Activity получает уведомление об обновлении данных в Content Provider через Content Observer).
  • При выполнении операций insert, delete, update данные в sqlite обновляются дважды: первый раз до отправки REST-запроса, второй раз — после получения результата. Первая операций выставляет информационные флаги, сигнализирующие о типе операции, проводимой над данными, и о статусе операции.
  • REST-методы следует всегда выполнять в отдельном потоке.
  • Следует использовать Apache HTTP client, а не Java URL connection.
  • Форматы данных в порядке предпочтения: какой-либо бинарный формат (например, AMF3), затем JSON, затем XML.
  • Желательно включать gzip. GZip на Android реализован “нативно”, библиотека быстрая. В некоторых случаях можно получить коэффициент сжатия 5:1 и даже 10:1, в зависимости от количества получаемых данных. Использование GZip ускоряет загрузку данных и экономит батарею.
  • Если используете Sqlite — используйте транзакции.
  • Если программе требуется скачать 10-20 картинок, не стоит запускать 10-20 параллельных закачек. Запускайте 1-3, а остальные ставьте в очередь.
  • Activity регистрирует binder callback (т.е. ResultReceiver), для получения ответа от сервиса. Этот callback нужно обязательно удалить при вызове onPause у Activity, иначе можно налететь на ANR.
  • Длительные операции всегда следует запускать из сервиса. Сервис обязательно следует останавливать после того, как требуемые операции выполнены.
  • Необходимо минимизировать сетевой трафик.
  • Следует разбивать данные на страницы (конечно, если REST Api предоставляют такую возможность).
  • Для некритичной по времени синхронизации данных между клиентом и сервером рекомендуется использовать SyncAdapter.

Алгоритм фомирования запроса к серверу

В общем случае, при выполнении запроса к REST-серверу, требуется выполнить ряд операций:

  • сформировать URL
  • задать HTTP-заголовки
  • выбрать тип HTTP-запроса
  • сформировать тело HTTP-запроса, т.е. преобразовать Java объект в JSON
  • выполнить запрос, воспользовавшись HTTP-клиентом
  • распарсить результаты запроса — преобразовать полученный JSON в Java объект

REST API для взаимодействия сервисов

Самым актуальный способом создать REST сервис в стеке технологий Майкрософт на сегодняшний день является ASP.NET Web API. До того эта технология значилась как WCF Web API и больше по названию тяготела к WCF. Но уже тогда там использовались сходные походы как в ASP.NET MVC, включая роутинг (routing). До нее существовали такие вещи как WCF 4 REST, WCF REST Starter Kit 3.5. Их все еще можно встретить на старых проектах и stackoverflow пестрит вопросами о них. Но то что ASP.NET Web API используется на новых проектах, а некоторые старые конвертируются, чтобы его использовать – радует. Так как предшественники были хуже как в плане технологии (приходилось писать много boilerplating code), удобства использования так и документации.

В предыдущих постах были рассмотрены некоторые теоретические аспекты REST – теперь создадим простой REST сервис с помощью Web API и рассмотрим ключевые элементы такого сервиса.
Начать стоит с подключения NuGet packages (и/или установки ASP.NET MVC):

  1. Web API, в случае если хостимся в ASP.NET:AspNetWebApi
  2. Self-hosted Web API:AspNetWebApi.Selfhost
  3. HttpClient включая XML и JSON форматеры:System.Net.Http.Formatting
  4. JsonValue для навигации и манипуляции JSON:System.Json

В нашем случае, мы создадим просто сервис, который хостится на ASP.NET MVC, а также посмотрим на принцип создания интеграционных тестов к нему, которые будут поднимать self-hosted REST сервис в тестовом контексте. Акцент на Data access layer делятся не будет, если в процессе вам необходимо прикрутить DAL, например, с использованием Entity Framework Code First, то я писал об одном из возможных подходов раньше.

Перед тем как создавать такой сервис необходимо также понимать что использовать Web API стоит если есть тесная связка с веб-клиентом, если сервис содержит логику выходящую за рамки CRUD операций. Если же у вас сервис по сути своей поставщик данных, т.е. операции в основном CRUD, то лучше использовать WCF Data Services, так как там много вещей из коробки генерится под базу — и CRUD операции и нормальная поддержка OData и IQuerable (в ASP.NET Web API она ограничена), которые позволяют делать запросы к сервису и данным с помощью Uri и специального OData синтаксиса.

Итак преступим. Для начала создадим новый проект ASP.NET MVC4:

Изображение 1
Естественно темплейт (шаблон) для MVC 4 нагенерит нам типичную структуру ASP.NET MVC проекта (файл ValuesController я уже успел переименовать на DocumentsController). Отличие в дополнительном контроллере для Web API. По умолчанию это ValuesController, естественно его необходимо переименовать.

В нашем случае он стал DocumentsController. Из темплейта этот контроллер содержит операции заглушки для Get, Post, Put, Delete. В просто случае переопределим эти операции для DocumentsController и ресурса Document. Получится вот такой вот контроллер:

Это простой вариант, и здесь не используются фильтры для обработки сообщений или dependency resolvers. В свою очередь IDocumentRepository реализовано как простая заглушка и если дальше развивать тему с нормальным доступом к данным то реализацию можно подставить любую.
Теперь проверим операции. Это сделать можно используя Fiddler и правильно сформировав запрос. Например операция получения всех документов, используем адрес http://127.0.0.1:81/api/documents/. Используется стандартный роутинг из коробки:

Итак, запрос на http://127.0.0.1:81/api/documents/ должен вызвать метод IEnumerable Get() :

Так и есть, нам вернулся список в виде XML из двух элементов. Теперь попробуем content negotiation из коробки в действии. К тому же самому вызову добавим HTTP заголовок – Accept:application/json. Итак запрос:

Ответ ожидаем в Json:

Из коробки идут два стандартных форматера – XML и Json, но есть возможность добавлять свои.

Аналогичным образом будут работать остальные операции. Единственное попробуем еще запросить документ с недействительным идентификатором. Будем вызывать метод Document Get(string id) по адресу http://127.0.0.1:81/api/documents/9505a3b549b54881b3ed83fc19510534, где 9505a3b549b54881b3ed83fc19510534 – недействительный идентификатор, изменили последнюю цифру.

Ожидается ответ 404 NotFound. Результат запроса:

Вот таким вот образом можно создать и протестировать на работоспособность простенький REST сервис на базе ASP.NET Web API.

Основные концепты — ApiController

Так как мы имеем дело с REST сервисом. То из всего этого добра нас интересуют на начальном этапе контроллеры и роутинг. Контроллеры для Web API REST сервиса наследуются от от класса ApiController, который в свою очередь от интерфейса IHttpController. И ApiController несет с собой много добра, кроме конечно того что она автоматом распознается и выполняется. Из всего этого добра самое интересное являются свойства Request и Configuration.

Основные концепты – Routing (Роутинг)

При вызове операций с контроллера важный момент играет routing. Именно routing позволяет подсистеме WebApi связать Uri адрес и конкретную операцию из контроллера. Причем есть несколько вариантов — либо операция-action помечается атрибутом, либо используется договоренность именовать операции с префиксом – Http Verb. Например, в методе PostDocument – именно префикс Post позволяет сказать Web Api что эта операция связанна с Uri и вызывается по соответствующему адресу с HTTP Verb – POST.
Еще одним вариантом для того, чтобы помочь выделить среди методов контроллера операции, которые связанны с URL – использование атрибутов — HttpGet, HttpPut, HttpPost, или HttpDelete, каждый из них соответствует такому же HTTP Verb – GET, PUT, POST, DELETE. Для того, чтобы навесить на операцию больше чем один HTTP Verb, или операцию отличную от 4 базовых (GET, PUT, POST, DELETE), используется атрибут – AcceptVerbs. Использование атрибутов также дает возможность отказаться от конвенции именования методов, когда префиксом выступает HTTP Verb.

А для того чтобы избежать мапинга (mapping) метода как action используется атрибут NonAction без параметров.
Есть еще способ роутинга, когда каждый мапинг делается по средством атрибутов на метод, а не глобальным роутингом через Global.asax.cs, но о нем позже, так как он не стандартный. Хотя на этапе WCF Web API использовался именно он.

Routing по-умолчанию в Web API устанавливается как в методе RegisterRoutes на изображении 5 ниже. При использовании такого routing необходимо придерживаться конвенции именования методов в контроллере, когда каждый метод начинается с HTTP Verb префикса.

Ну и естественно важная часть маппинга – routing в Global.asax.cs:

Соответственно под роутинг «api//» подпадают URLs и примерные имена методов:
Можно также сделать роутинг по имени action. Он не создается по-умолчанию темплейтом проекта. Например:
В случае с таким роутингом необходимо использовать атрибуты HttpGet, HttpPut, HttpPost, HttpDelete или AcceptVerbs чтобы указать на какие методы мапить . В WCF WebAPI использовался роутинг с помощью атрибутов, его тоже можно прикрутить, но об этом отдельно.

Основные концепты — HttpResponseMessage, HttpRequestMessage

По сути это два спец класса которые используются достаточно часто. Они нужны для того чтобы иметь возможность оперировать запросом и ответом. HttpRequestMessage можно получить через свойство Request от ApiController (Изображение 6). Tак как Web API контроллеры всегда наследуются от ApiController, то его можно получить в середине любого из наших контроллеров. HttpRequestMessage позволяет нам управлять запросом, например извлекать из него данные из HTTP Body либо HTTP Headers которые нам нужны.

HttpResponseMessage можно создать, чтобы вернуть результат, либо просто Response код (Изображение 7), либо еще и с нагрузкой, запаковав в его свойство Content, нужный нам HttpContent, например для бинарных данных подойдет наследник от HttpContent – StreamContent. Из свойства Request можно вычитать бинарные данные документа, который пришел с клиента:

Возврат ошибок — HttpResponseException

Вернуть ответ с ошибкой можно как с помощью HttpResponseMessage, указав код ошибки, так и с помощью специального класса HttpResponseException. Например, на изображении 7 на клиент возвращается ошибка InternalServerError = 500 с коротким описанием. Описание умеют читать далеко не все клиенты или клиентские библиотеки (были проблемы с iPad), в таком случае в тело сообщения с ошибкой можно писать объект более детально описывающий проблему, например свой кастомный объект с сообщением и кодом ошибки.

Хостинг

Само собой разумеется, что Web API REST сервис может хоститься на IIS либо вместе с ASP.NET MVC клиентом либо раздельно. Также его можно легко захостить вместе с ASP.NET MVC Web Role в облаке на Windows Azure. Но интересно, что Web API также можно хостить у себя в приложении, в памяти. Это значительно расширяет круг сценариев, в которых Web API может использоваться. Например с self-hosted Web API можно легко делать интеграционные тесты, которые поднимут во время тестирования self-hosted Web API сервис.

Например, на изображение 8 ниже, показано как поднимается с self-hosted Web API сервис для интеграционного теста в методе BecauseOf.

Клиент

Клиентов к Web API REST может быть большое множество – есть куча библиотек под разные платформы для REST, можно обращаться к REST сервису c веб страницы по средством JavaScript и jQuery, можно использовать “старенький” класс WebClient для десктоп клиента. Вместе с Web API новым для .NET является также новый HttpClient, который очень легко использовать с десктоп клиента или тестового сценария (пример на изображении 8 метод should_make_tivial_get), и к тому же он изначально спроектирован асинхронным.

Полезные API сервисы

Современную разработку сложно представить без обращения к сервисами API. Благодаря программному интерфейсу приложения мы можем легко взаимодействовать с другим программным обеспечением. И это может быть простое API как получение информации об IP-адресе, так и более сложное.

Хорошо, давайте же перейдем к обзору подобных сервисов.

Платежные системы

API Яндекс.Кошелька — позволяет частным лицам использовать возможности сервиса Яндекс.Денег.

API Яндекс.Кассы — инструмент для интеграции с Яндекс.Кассой, который позволяет принимать платежи, делать возвраты, узнавать о статусе операций и многое другое.

API WebMoney — различные варианты приема платежей и управления средствами.

API PayPal — международная система для осуществления платежей и денежных переводов с банковского счета или карточки, подключенных к системе.

API QIWI Кошелька позволяет автоматизировать получение информации о вашем счёте в сервисе QIWI Кошелек и проводить операции с его помощью.

API PayMaster — техническая информация, которая поможет Вам настроить интерфейс приема платежей.

API Единая касса — универсальная система приема платежей на сайте (118 способов оплаты).

API Interkassa — представляет программный интерфейс, который позволяет любому мерчанту автоматически принимать платежи в режиме онлайн.

API ROBOKASSA — сервис, позволяющий клиентам принимать платежи от покупателей.

API Тинькофф Оплата — представляет платежи для вашего бизнеса.


API RBKmoney — является базовой и единственной точкой взаимодействия с платежной платформой.

API Сбербанк Эквайринг — приём к оплате платёжных карт в качестве средства оплаты товара, работ и услуг.

Погода

API OpenWeatherMap — бесплатное API для получения сведений о погодных условиях.

API Яндекс.Погода — позволяет получать погодные данные в автоматизированном режиме (бесплатно лимит запросов 50 в сутки).

API Gismeteo — сервис, который предоставляет прогноз погоды в городах России и мира.

API IP Geolocation — бесплатный сервис для некоммерческого использования без ключа API. Легко интегрируется и поддерживает JSON, XML, CSV, Newline и PHP .

API IPAPI — простой и быстрый программный поиск IP-адреса для вашего веб-сайта и мобильного приложения.

API Ipify — простое публичное API для IP-адреса.

Социальные сети

API instagram — популярная социальная сеть, позволяющая хранить фотографии пользователей и делиться ими.

API MediaWiki — веб-служба, обеспечивающая доступ к таким функциям вики, как аутентификация, операции над страницами и поиск по вики.

API Вконтакте — документация по авторизации на базе протокола OAuth, стандартная схема формирования запросов, распространение контента ВКонтакте и другое.

API Facebook — обширная документация по взаимодействию с сервисами Facebook.

API Одноклассники — официальная документация по взаимодействию с социальной сетью Одноклассники.

Страны

API REST Countries — получите информацию о странах через RESTful API.

Данная статья постоянно пополняется новыми полезными API-сервисами. Если Вы знакомы с интересным сервисом, будем рады любым комментариям.

До новых встреч.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Разработка веб-API Web API design

Большинство современных веб-приложений предоставляют API, которые клиенты могут использовать для взаимодействия с приложением. Most modern web applications expose APIs that clients can use to interact with the application. Качественно спроектированный API должен поддерживать следующее: A well-designed web API should aim to support:

Независимость от платформы. Platform independence. Любой клиент должен иметь возможность вызывать API, независимо от того, как API реализован внутренне. Any client should be able to call the API, regardless of how the API is implemented internally. Для этого требуется использование стандартных протоколов, а также наличие механизма, при котором клиент и веб-службы могут согласовать формат данных для обмена. This requires using standard protocols, and having a mechanism whereby the client and the web service can agree on the format of the data to exchange.

Развитие службы. Service evolution. Веб-API должен иметь возможность развиваться и расширять набор функций независимо от клиентских приложений. The web API should be able to evolve and add functionality independently from client applications. По мере развития API имеющиеся клиентские приложения должны продолжать работать без изменений. As the API evolves, existing client applications should continue to function without modification. Все функции должны быть доступными, чтобы клиентские приложения могли полноценно их использовать. All functionality should be discoverable so that client applications can fully use it.

В этом руководстве описываются проблемы, которые следует учитывать при разработке веб-API. This guidance describes issues that you should consider when designing a web API.

Общие сведения о REST Introduction to REST

В 2000 году Рой Филдинг предложил архитектурный подход к разработке веб-служб — передачу репрезентативного состояния (REST). In 2000, Roy Fielding proposed Representational State Transfer (REST) as an architectural approach to designing web services. REST — это архитектурная концепция создания распределенных систем на основе гиперсред. REST is an architectural style for building distributed systems based on hypermedia. Модель REST не зависит от каких-либо базовых протоколов и не требует привязки к HTTP. REST is independent of any underlying protocol and is not necessarily tied to HTTP. Однако наиболее распространенные реализации REST используют HTTP в качестве протокола приложения. Поэтому это руководство посвящено разработке REST API для HTTP. However, most common REST implementations use HTTP as the application protocol, and this guide focuses on designing REST APIs for HTTP.

Основное преимущество при использовании REST с протоколом HTTP заключается в применении открытых стандартов и отсутствии необходимости в определенной реализации API или клиентских приложений. A primary advantage of REST over HTTP is that it uses open standards, and does not bind the implementation of the API or the client applications to any specific implementation. Например, веб-службу REST можно написать в ASP.NET, а клиентские приложения могут использовать любой язык или набор инструментов, позволяющие создавать HTTP-запросы и анализировать HTTP-ответы. For example, a REST web service could be written in ASP.NET, and client applications can use any language or toolset that can generate HTTP requests and parse HTTP responses.

Ниже приведены некоторые основные принципы проектирования интерфейсов RESTful API, использующих протокол HTTP: Here are some of the main design principles of RESTful APIs using HTTP:

REST API разрабатываются на основе ресурса, который может быть любым типом объекта, данных или службы, к которому может получить доступ клиент. REST APIs are designed around resources, which are any kind of object, data, or service that can be accessed by the client.

У ресурса есть идентификатор (универсальный код ресурса (URI)), который уникально идентифицирует этот ресурс. A resource has an identifier, which is a URI that uniquely identifies that resource. Например, URI для определенного клиентского заказа может быть таким: For example, the URI for a particular customer order might be:

Клиенты взаимодействуют со службой путем обмена представлениями ресурсов. Clients interact with a service by exchanging representations of resources. Многие веб-API используют JSON в качества формата для обмена. Many web APIs use JSON as the exchange format. Например, в результате запроса GET к приведенному выше URI может вернуться такой текст ответа: For example, a GET request to the URI listed above might return this response body:

Интерфейсы REST API используют единый интерфейс, который позволяет отделить реализации клиента и службы. REST APIs use a uniform interface, which helps to decouple the client and service implementations. Для REST API, созданных на основе протокола HTTP, единый интерфейс будет использовать стандартные HTTP-команды для выполнения операций с ресурсами. For REST APIs built on HTTP, the uniform interface includes using standard HTTP verbs to perform operations on resources. Наиболее часто выполняемые операции: GET, POST, PUT, PATCH и DELETE. The most common operations are GET, POST, PUT, PATCH, and DELETE.

REST API использует модель запросов без отслеживания состояния. REST APIs use a stateless request model. HTTP-запросы должны быть независимыми и могут создаваться в любом порядке, поэтому сохранение сведений о переходном состоянии между запросами не представляется возможным. HTTP requests should be independent and may occur in any order, so keeping transient state information between requests is not feasible. Сведения хранятся только в самих ресурсах, и каждый запрос должен быть атомарной операцией. The only place where information is stored is in the resources themselves, and each request should be an atomic operation. Благодаря этому ограничению веб-службы имеют высокую масштабируемость, так как нет необходимости сохранять сходство между клиентами и конкретными серверами. This constraint enables web services to be highly scalable, because there is no need to retain any affinity between clients and specific servers. Каждый сервер может обрабатывать любой запрос от любого клиента. Any server can handle any request from any client. Тем не менее, другие факторы могут ограничить масштабируемость. That said, other factors can limit scalability. Например, многие веб-службы записывают данные в серверное хранилище данных, что может быть трудно масштабировать. Дополнительные сведения о стратегиях масштабирования хранилища данных см. в разделе горизонтальное, вертикальное и функциональное секционирование данных. For example, many web services write to a backend data store, which may be hard to scale out. For more information about strategies to scale out a data store, see Horizontal, vertical, and functional data partitioning.

Интерфейсами REST API управляют ссылки на гиперсреды, которые содержатся в представлении. REST APIs are driven by hypermedia links that are contained in the representation. Например, ниже показано JSON-представление заказа. For example, the following shows a JSON representation of an order. Оно содержит ссылки для получения или обновления клиента, связанного с заказом. It contains links to get or update the customer associated with the order.

В 2008 году Леонард Ричардсон предложил следующую модель зрелости для веб-API: In 2008, Leonard Richardson proposed the following maturity model for web APIs:

  • Уровень 0. Определение одного URI, и все операции будут POST-запросами к этому URI. Level 0: Define one URI, and all operations are POST requests to this URI.
  • Уровень 1. Создание отдельных URI для отдельных ресурсов. Level 1: Create separate URIs for individual resources.
  • Уровень 2. Использование методов HTTP для определения операций с ресурсами. Level 2: Use HTTP methods to define operations on resources.
  • Уровень 3. Использование гиперсред (HATEOAS, описан ниже). Level 3: Use hypermedia (HATEOAS, described below).

Уровень 3 соответствует настоящему RESTful API, согласно определению Филдинга. Level 3 corresponds to a truly RESTful API according to Fielding’s definition. На практике многие опубликованные веб-API находятся где-то около уровня 2. In practice, many published web APIs fall somewhere around level 2.

Упорядочивание API вокруг ресурсов Organize the API around resources

Сосредоточьтесь на бизнес-сущностях, предоставляемых веб-API. Focus on the business entities that the web API exposes. Например, в системе электронной коммерции основными сущностями могут быть клиенты и заказы. For example, in an e-commerce system, the primary entities might be customers and orders. Создание заказа может осуществляться путем отправки HTTP-запроса POST, содержащего сведения о заказе. Creating an order can be achieved by sending an HTTP POST request that contains the order information. HTTP-ответ указывает на успешность размещения заказа. The HTTP response indicates whether the order was placed successfully or not. Когда это возможно, универсальные коды ресурсов должны основываться на существительных (ресурсе), а не на глаголах (операциях c ресурсом). When possible, resource URIs should be based on nouns (the resource) and not verbs (the operations on the resource).

Ресурс не должен основываться на одном физическом элементе данных. A resource doesn’t have to be based on a single physical data item. Например, ресурс заказа может быть реализован внутри системы с использованием нескольких таблиц в реляционной базе данных, однако представлен в клиентском приложении как единая сущность. For example, an order resource might be implemented internally as several tables in a relational database, but presented to the client as a single entity. Избегайте создания API, которые просто отражают внутреннюю структуру базы данных. Avoid creating APIs that simply mirror the internal structure of a database. Цель REST — моделировать сущности и операции, которые приложение может выполнять в этих сущностях. The purpose of REST is to model entities and the operations that an application can perform on those entities. Внутренняя реализация должна быть скрыта от клиента. A client should not be exposed to the internal implementation.

Сущности часто группируются в коллекции (заказов, клиентов). Entities are often grouped together into collections (orders, customers). Коллекция — это отдельный ресурс из элемента в коллекции и должен иметь свой собственный URI. A collection is a separate resource from the item within the collection, and should have its own URI. Например, следующий URI может представлять коллекцию заказов: For example, the following URI might represent the collection of orders:

В результате отправки HTTP-запроса GET на URI коллекции возвращается список элементов в коллекции. Sending an HTTP GET request to the collection URI retrieves a list of items in the collection. Каждый элемент в коллекции также имеет свой собственный универсальный код ресурса (URI). Each item in the collection also has its own unique URI. HTTP-запрос GET к URI элемента возвращает подробные сведения об этом элементе. An HTTP GET request to the item’s URI returns the details of that item.

Используйте единое соглашение об именовании в универсальных кодах ресурсов. Adopt a consistent naming convention in URIs. В целом это помогает использовать существительные во множественном числе для URI, ссылающихся на коллекции. In general, it helps to use plural nouns for URIs that reference collections. Рекомендуется упорядочивать универсальные коды ресурсов для коллекций и элементов в иерархии. It’s a good practice to organize URIs for collections and items into a hierarchy. Например, /customers — это путь к коллекции клиентов, а /customers/5 — путь к клиенту с идентификатором равным 5. For example, /customers is the path to the customers collection, and /customers/5 is the path to the customer with ID equal to 5. Этот подход позволяет обеспечивать простоту веб-API. This approach helps to keep the web API intuitive. Кроме того, множество платформ веб-API могут направлять запросы на основании параметризованных путей URI, поэтому можно определить маршрут для пути /customers/ . Also, many web API frameworks can route requests based on parameterized URI paths, so you could define a route for the path /customers/ .

Также следует продумать связи между разными типами ресурсов и способы предоставления этих связей. Also consider the relationships between different types of resources and how you might expose these associations. Например, /customers/5/orders может представлять все заказы для клиента 5. For example, the /customers/5/orders might represent all of the orders for customer 5. Вы также можете пойти в другом направлении и представить связь между заказом и конкретным клиентом посредством универсального кода ресурса, например /orders/99/customer . You could also go in the other direction, and represent the association from an order back to a customer with a URI such as /orders/99/customer . Однако чрезмерное расширение этой модели может вызвать трудности. However, extending this model too far can become cumbersome to implement. Более рациональное решение — предоставить ссылки с возможностью перехода на связанные ресурсы в тексте HTTP-ответа. A better solution is to provide navigable links to associated resources in the body of the HTTP response message. Этот механизм более подробно описан в разделе Использование HATEOAS для включения навигации к связанным ресурсам. This mechanism is described in more detail in the section Use HATEOAS to enable navigation to related resources.

В более сложных системах очевидным решением может показаться предоставление URI, позволяющих клиенту переходить между несколькими уровнями связей, например /customers/1/orders/99/products . In more complex systems, it can be tempting to provide URIs that enable a client to navigate through several levels of relationships, such as /customers/1/orders/99/products . Однако такой уровень сложности трудно обслуживать и адаптировать в случае дальнейшего изменения связей между ресурсами. However, this level of complexity can be difficult to maintain and is inflexible if the relationships between resources change in the future. Вместо этого постарайтесь сделать URI максимально простыми. Instead, try to keep URIs relatively simple. Как только у приложения появляется ссылка на ресурс, оно может использовать эту ссылку для поиска элементов, связанных с указанным ресурсом. Once an application has a reference to a resource, it should be possible to use this reference to find items related to that resource. Предыдущий запрос можно заменить на URI /customers/1/orders , чтобы найти все заказы для клиента 1, а затем — на /orders/99/products , чтобы найти продукты в этом заказе. The preceding query can be replaced with the URI /customers/1/orders to find all the orders for customer 1, and then /orders/99/products to find the products in this order.

Мастер Йода рекомендует:  Использование SSI в построении сайта

Старайтесь использовать универсальные коды ресурсов не сложнее collection/item/collection. Avoid requiring resource URIs more complex than collection/item/collection.

Кроме того, следует учесть, что все веб-запросы увеличивают нагрузку на веб-сервер, Another factor is that all web requests impose a load on the web server. которая растет вместе с числом запросов. The more requests, the bigger the load. Поэтому старайтесь избегать создания веб-API с множественными операциями ввода-вывода, которые предоставляют большое количество небольших ресурсов. Therefore, try to avoid «chatty» web APIs that expose a large number of small resources. Такой API может потребовать от клиентского приложения отправки нескольких запросов для поиска всех необходимых данных. Such an API may require a client application to send multiple requests to find all of the data that it requires. Вместо этого можно выполнить денормализацию данных и объединить соответствующую информацию в более крупные ресурсы, которые можно получить с помощью одного запроса. Instead, you might want to denormalize the data and combine related information into bigger resources that can be retrieved with a single request. Однако следует сохранять равновесие в этом подходе, чтобы избежать получения чрезмерного объема данных, которые не нужны клиенту. However, you need to balance this approach against the overhead of fetching data that the client doesn’t need. Получение больших объектов может увеличить задержку запроса и привести к дополнительным расходам на пропускную способность. Retrieving large objects can increase the latency of a request and incur additional bandwidth costs. Дополнительные сведения об этих антишаблонах производительности см. в статье Антишаблон отправки множественных операций ввода-вывода и Антишаблон лишней выборки. For more information about these performance antipatterns, see Chatty I/O and Extraneous Fetching.

Избегайте зависимостей между веб-API и базовыми источниками данных. Avoid introducing dependencies between the web API and the underlying data sources. Например, если данные хранятся в реляционной базе данных, веб-API не требуется предоставлять каждую таблицу в виде коллекции ресурсов. For example, if your data is stored in a relational database, the web API doesn’t need to expose each table as a collection of resources. Это, вероятно, будет неэффективная архитектура. In fact, that’s probably a poor design. Вместо этого представьте веб-API в виде абстракции базы данных. Instead, think of the web API as an abstraction of the database. При необходимости создайте уровень сопоставления между базой данных и веб-API. If necessary, introduce a mapping layer between the database and the web API. Таким образом клиентские приложения изолированы от изменений в базовой схеме базы данных. That way, client applications are isolated from changes to the underlying database scheme.

Наконец, сопоставление каждой операции, реализованной веб-API, с конкретным источником не всегда возможно. Finally, it might not be possible to map every operation implemented by a web API to a specific resource. Такие безресурсные сценарии можно обрабатывать с помощью HTTP-запросов, вызывающих определенную функцию и возвращающих результаты в виде ответного HTTP-сообщения. You can handle such non-resource scenarios through HTTP requests that invoke a function and return the results as an HTTP response message. Например, веб-API, реализующий простые расчетные операции, такие как добавление и вычитание, может предоставить универсальные коды ресурсов (URI), представляющие эти операции в виде псевдоресурсов, и использовать строку запроса для указания требуемых параметров. For example, a web API that implements simple calculator operations such as add and subtract could provide URIs that expose these operations as pseudo resources and use the query string to specify the parameters required. Например, запрос GET к URI /Add? операнд1 = 99 & операнд2 = 1 возвратит ответное сообщение с текстом, содержащим значение 100. For example, a GET request to the URI /add?operand1=99&operand2=1 would return a response message with the body containing the value 100. Однако следует использовать эти формы универсальных кодов ресурсов (URI) с осторожностью. However, only use these forms of URIs sparingly.

Определение операций с точки зрения методов HTTP Define operations in terms of HTTP methods

Протокол HTTP определяет несколько методов, назначающих запросу семантическое значение. The HTTP protocol defines a number of methods that assign semantic meaning to a request. Ниже приведены наиболее распространенные методы HTTP, используемые большинством веб-API RESTful: The common HTTP methods used by most RESTful web APIs are:

  • GET. Возвращает представление ресурса по указанному универсальному коду ресурса (URI). GET retrieves a representation of the resource at the specified URI. Текст ответного сообщения содержит сведения о запрашиваемом ресурсе. The body of the response message contains the details of the requested resource.
  • POST. Создает новый ресурс по указанному URI. POST creates a new resource at the specified URI. Текст запроса содержит сведения о новом ресурсе. The body of the request message provides the details of the new resource. Обратите внимание, что метод POST также можно использовать для запуска операций, не относящихся непосредственно к созданию ресурсов. Note that POST can also be used to trigger operations that don’t actually create resources.
  • PUT. Создает или заменяет ресурсы по указанному URI. PUT either creates or replaces the resource at the specified URI. В тексте сообщения запроса указан создаваемый или обновляемый ресурс. The body of the request message specifies the resource to be created or updated.
  • PATCH. Выполняет частичное обновление ресурса. PATCH performs a partial update of a resource. Текст запроса определяет набор изменений, применяемых к ресурсу. The request body specifies the set of changes to apply to the resource.
  • DELETE. Удаляет ресурс по указанному URI. DELETE removes the resource at the specified URI.

Результат конкретного запроса должен зависеть от того, является ли целевой ресурс коллекцией или отдельным элементом. The effect of a specific request should depend on whether the resource is a collection or an individual item. В следующей таблице приведены общие соглашения, принятые большинством реализаций RESTFUL с помощью примера электронной коммерции. The following table summarizes the common conventions adopted by most RESTful implementations using the e-commerce example. Не все эти запросы могут быть реализованы—он зависит от конкретного сценария. Not all of these requests might be implemented—it depends on the specific scenario.

Ресурс Resource POST POST GET GET PUT PUT DELETE DELETE
/customers /customers Создание нового клиента Create a new customer Получение всех клиентов Retrieve all customers Массовое обновление клиентов Bulk update of customers Удаление всех клиентов Remove all customers
/customers/1 /customers/1 Ошибка Error Получение сведений для клиента 1 Retrieve the details for customer 1 Обновление сведения о клиенте 1, если он существует Update the details of customer 1 if it exists Удаление клиента 1 Remove customer 1
/customers/1/orders /customers/1/orders Создание нового заказа для клиента 1 Create a new order for customer 1 Получение всех заказов для клиента 1 Retrieve all orders for customer 1 Массовое обновление заказов для клиента 1 Bulk update of orders for customer 1 Удаление всех заказов для клиента 1 Remove all orders for customer 1

Различия между POST, PUT и PATCH могут запутать новичков. The differences between POST, PUT, and PATCH can be confusing.

Запрос POST создает ресурс. A POST request creates a resource. Сервер назначает URI для нового ресурса и возвращает этот URI клиенту. The server assigns a URI for the new resource, and returns that URI to the client. В модели REST запросы POST постоянно применяются к коллекциям. In the REST model, you frequently apply POST requests to collections. При этом новый ресурс добавляется в коллекцию. The new resource is added to the collection. Запрос POST также может использоваться для отправки данных для обработки в имеющийся ресурс без создания нового ресурса. A POST request can also be used to submit data for processing to an existing resource, without any new resource being created.

Запрос PUT создает ресурс или обновляет имеющийся ресурс. A PUT request creates a resource or updates an existing resource. Клиент указывает универсальный код ресурса. The client specifies the URI for the resource. Текст запроса содержит полное представление ресурса. The request body contains a complete representation of the resource. Если ресурс с таким URI уже существует, он заменяется. If a resource with this URI already exists, it is replaced. В противном случае создается новый ресурс, если сервер поддерживает это. Otherwise a new resource is created, if the server supports doing so. Запросы PUT чаще всего применяются к ресурсам, которые являются отдельными элементами, например конкретные клиенты, а не к коллекциям. PUT requests are most frequently applied to resources that are individual items, such as a specific customer, rather than collections. Сервер может поддерживать обновления, но не создание через PUT. A server might support updates but not creation via PUT. Требуется ли поддерживать создание через PUT зависит от того, может ли клиент назначать информативный URI ресурсу до его существования. Whether to support creation via PUT depends on whether the client can meaningfully assign a URI to a resource before it exists. Если нет, используйте POST для создания ресурсов, а PUT или PATCH для обновления. If not, then use POST to create resources and PUT or PATCH to update.

Запрос PATCH выполняет частичное обновление имеющегося ресурса. A PATCH request performs a partial update to an existing resource. Клиент указывает универсальный код ресурса. The client specifies the URI for the resource. Текст запроса определяет набор изменений, применяемых к ресурсу. The request body specifies a set of changes to apply to the resource. Это может быть более эффективно, чем использование PUT, так как клиент отправляет только изменения, а не все представление ресурса. This can be more efficient than using PUT, because the client only sends the changes, not the entire representation of the resource. С технической точки зрения PATCH также может создать новый ресурс (указав набор обновлений для ресурса «null»), если это поддерживается сервером. Technically PATCH can also create a new resource (by specifying a set of updates to a «null» resource), if the server supports this.

Запросы PUT должны быть идемпотентными. PUT requests must be idempotent. Если клиент отправляет тот же запрос PUT несколько раз, результаты всегда должны быть одинаковыми (тот же ресурс будет изменяться с теми же значениями). If a client submits the same PUT request multiple times, the results should always be the same (the same resource will be modified with the same values). Запросы POST и PATCH не будут гарантированно идемпотентными. POST and PATCH requests are not guaranteed to be idempotent.

Соответствие семантике HTTP Conform to HTTP semantics

В этом разделе описываются некоторые распространенные вопросы по проектированию API, соответствующего спецификации HTTP. This section describes some typical considerations for designing an API that conforms to the HTTP specification. Однако он не охватывает все возможные детали или сценарии. However, it doesn’t cover every possible detail or scenario. Если вы сомневаетесь, обратитесь к спецификациям HTTP. When in doubt, consult the HTTP specifications.

Типы мультимедиа Media types

Как упоминалось ранее, клиенты и серверы обмениваются представлениями ресурсов. As mentioned earlier, clients and servers exchange representations of resources. Например, в тексте запроса POST содержится представление создаваемого ресурса. For example, in a POST request, the request body contains a representation of the resource to create. В тексте запроса GET содержится представление получаемого ресурса. In a GET request, the response body contains a representation of the fetched resource.

В протоколе HTTP форматы задаются с помощью типов мультимедиа, также называемых типами MIME. In the HTTP protocol, formats are specified through the use of media types, also called MIME types. Для недвоичных данных большинство веб-API поддерживают формат JSON (тип мультимедиа — application/json) и возможно XML (тип мультимедиа — application/xml). For non-binary data, most web APIs support JSON (media type = application/json) and possibly XML (media type = application/xml).

Заголовок Content-Type в запросе или ответе определяет формат представления. The Content-Type header in a request or response specifies the format of the representation. Ниже приведен пример запроса POST, который включает в себя данные JSON: Here is an example of a POST request that includes JSON data:

Если сервер не поддерживает данный тип мультимедиа, он возвращает код состояния HTTP 415 (неподдерживаемый тип носителя). If the server doesn’t support the media type, it should return HTTP status code 415 (Unsupported Media Type).

Запрос клиента может включать заголовок Accept, содержащий список типов мультимедиа, которые клиент будет принимать от сервера в ответном сообщении. A client request can include an Accept header that contains a list of media types the client will accept from the server in the response message. Пример. For example:

Если сервер не может соответствовать ни одному из указанных типов мультимедиа, он должен вернуть код состояния HTTP 406 (неприемлемо). If the server cannot match any of the media type(s) listed, it should return HTTP status code 406 (Not Acceptable).

Методы GET GET methods

Успешное выполнение метода GET обычно возвращает код состояния HTTP 200 (ОК). A successful GET method typically returns HTTP status code 200 (OK). Если ресурс не найден, метод должен вернуть код 404 (не найдено). If the resource cannot be found, the method should return 404 (Not Found).

Методы POST POST methods

Если метод POST создает новый ресурс, он возвращает код состояния HTTP 201 (создано). If a POST method creates a new resource, it returns HTTP status code 201 (Created). URI нового ресурса содержится в заголовке Location ответа. The URI of the new resource is included in the Location header of the response. Текст ответа содержит представление ресурса. The response body contains a representation of the resource.

Если метод выполняет определенную обработку, но не создает новый ресурс, он может вернуть код состояния HTTP 200 и содержать результат операции в тексте ответа. If the method does some processing but does not create a new resource, the method can return HTTP status code 200 and include the result of the operation in the response body. Кроме того, при отсутствии результатов для возврата метод может вернуть код состояния HTTP 204 (нет содержимого) без текста ответа. Alternatively, if there is no result to return, the method can return HTTP status code 204 (No Content) with no response body.


Если клиент помещает недопустимые данные в запрос, сервер должен вернуть код состояния HTTP 400 (неверный запрос). If the client puts invalid data into the request, the server should return HTTP status code 400 (Bad Request). Текст ответа может содержать дополнительные сведения об ошибке или ссылку на универсальный код ресурса (URI), по которому можно получить более подробную информацию. The response body can contain additional information about the error or a link to a URI that provides more details.

Методы PUT PUT methods

Если метод PUT создает новый ресурс, он возвращает код состояния HTTP 201 (создано), как и метод POST. If a PUT method creates a new resource, it returns HTTP status code 201 (Created), as with a POST method. Если метод обновляет имеющийся ресурс, он возвращает коды состояния 200 (ОК) или 204 (нет содержимого). If the method updates an existing resource, it returns either 200 (OK) or 204 (No Content). В некоторых случаях обновить имеющийся ресурс невозможно. In some cases, it might not be possible to update an existing resource. В этом случае рассмотрите возможность возврата кода состояния HTTP 409 (конфликт). In that case, consider returning HTTP status code 409 (Conflict).

Рассмотрите возможность реализации массовых HTTP-операций PUT, поддерживающих пакетные обновления нескольких ресурсов в коллекции. Consider implementing bulk HTTP PUT operations that can batch updates to multiple resources in a collection. В запросе PUT должен быть указан универсальный код ресурса (URI) коллекции, а текст запроса должен содержать сведения о ресурсах, которые требуется изменить. The PUT request should specify the URI of the collection, and the request body should specify the details of the resources to be modified. Такой подход помогает сократить избыточность и повысить производительность. This approach can help to reduce chattiness and improve performance.

Методы PATCH PATCH methods

С помощью запроса PATCH клиент отправляет набор обновлений в имеющийся ресурс в виде документа с исправлениями. With a PATCH request, the client sends a set of updates to an existing resource, in the form of a patch document. Сервер обрабатывает документ с исправлениями, чтобы выполнить обновление. The server processes the patch document to perform the update. Документ с исправлениями не описывает весь ресурс, а только набор применяемых изменений. The patch document doesn’t describe the whole resource, only a set of changes to apply. Спецификация метода PATCH (RFC 5789) не определяет конкретный формат документов с исправлениями. The specification for the PATCH method (RFC 5789) doesn’t define a particular format for patch documents. Формат необходимо определить на основе типа мультимедиа в запросе. The format must be inferred from the media type in the request.

Вероятно, наиболее распространенный формат данных для веб-API — JSON. JSON is probably the most common data format for web APIs. Есть два основных формата исправлений на основе JSON, называемые исправлением JSON и исправлением со слиянием JSON. There are two main JSON-based patch formats, called JSON patch and JSON merge patch.

Исправление со слиянием JSON несколько проще. JSON merge patch is somewhat simpler. Документ с исправлениями имеет ту же структуру, что и исходный ресурс JSON, однако включает только подмножество полей, которые необходимо изменить или добавить. The patch document has the same structure as the original JSON resource, but includes just the subset of fields that should be changed or added. Кроме того, поле можно удалить, указав null для значения поля в документе с исправлениями. In addition, a field can be deleted by specifying null for the field value in the patch document. (Это означает, что исправление со слиянием не подходит, если исходный ресурс может иметь явные значения null.) (That means merge patch is not suitable if the original resource can have explicit null values.)

Предположим, что исходный ресурс имеет следующее представление JSON: For example, suppose the original resource has the following JSON representation:

Вот возможное исправление со слиянием JSON для этого ресурса: Here is a possible JSON merge patch for this resource:

Это означает, что сервер обновляет price , удаляет color и добавляет size , а name и category не изменяются. This tells the server to update price , delete color , and add size , while name and category are not modified. Точные сведения об исправлении со слиянием JSON см. в документе RFC 7396. For the exact details of JSON merge patch, see RFC 7396. Тип носителя для исправления со слиянием JSON — application/merge-patch+json . The media type for JSON merge patch is application/merge-patch+json .

Исправление со слиянием не подходит, если исходный ресурс может содержать явные значения null, из-за смысла значения null в документе с исправлениями. Merge patch is not suitable if the original resource can contain explicit null values, due to the special meaning of null in the patch document. Кроме того, в документе с исправлениями не указывается порядок, в котором сервер должен применять обновления. Also, the patch document doesn’t specify the order that the server should apply the updates. Это может иметь значение, в зависимости от данных и предметной области. That may or may not matter, depending on the data and the domain. Исправление JSON, определенное в RFC 6902, — более гибкое. JSON patch, defined in RFC 6902, is more flexible. Оно определяет изменения как последовательность выполняемых операций, It specifies the changes as a sequence of operations to apply. в частности добавление, удаление, замена, копирование и тестирование (для проверки значений). Operations include add, remove, replace, copy, and test (to validate values). Тип носителя для исправления JSON — application/json-patch+json . The media type for JSON patch is application/json-patch+json .

Ниже приведены некоторые типичные ошибки, которые могут возникнуть при обработке запроса PATCH, вместе с соответствующим кодом состояния HTTP. Here are some typical error conditions that might be encountered when processing a PATCH request, along with the appropriate HTTP status code.

Условие ошибки Error condition HTTP status code (Код состояния HTTP) HTTP status code
Формат документа с исправлениями не поддерживается. The patch document format isn’t supported. 415 (неподдерживаемый тип носителя) 415 (Unsupported Media Type)
Неправильный формат документа с исправлениями. Malformed patch document. 400 (недопустимый запрос) 400 (Bad Request)
Документ с исправлениями действителен, но изменения нельзя применить к ресурсу в его текущем состоянии. The patch document is valid, but the changes can’t be applied to the resource in its current state. 409 (конфликт) 409 (Conflict)

Методы DELETE DELETE methods

Если операция удаления прошла успешно, веб-сервер должен вернуть ответ с кодом состояния HTTP 204, указывающим на успешное завершение процесса и отсутствие дополнительных сведений в тексте ответа. If the delete operation is successful, the web server should respond with HTTP status code 204, indicating that the process has been successfully handled, but that the response body contains no further information. Если ресурс не существует, веб-сервер может вернуть код HTTP 404 (не найдено). If the resource doesn’t exist, the web server can return HTTP 404 (Not Found).

Асинхронные операции Asynchronous operations

Иногда для завершения операции POST, помещения, исправления или удаления может потребоваться обработка, которая занимает некоторое время. Sometimes a POST, PUT, PATCH, or DELETE operation might require processing that takes a while to complete. Если дожидаться завершения до отправки ответа клиенту, это может привести к неприемлемой задержке. If you wait for completion before sending a response to the client, it may cause unacceptable latency. В этом случае рассмотрите возможность создания асинхронной операции. If so, consider making the operation asynchronous. Верните код состояния HTTP 202, указывающий, что запрос был принят в обработку, но не завершен. Return HTTP status code 202 (Accepted) to indicate the request was accepted for processing but is not completed.

Следует предоставить конечную точку, которая возвращает состояние асинхронного запроса, чтобы клиент мог отслеживать состояние, опрашивая конечную точку состояния. You should expose an endpoint that returns the status of an asynchronous request, so the client can monitor the status by polling the status endpoint. Включите URI конечной точки состояния в заголовок Location ответа 202. Include the URI of the status endpoint in the Location header of the 202 response. Пример. For example:

Если клиент отправляет запрос GET к этой конечной точке, ответ должен содержать текущее состояние запроса. If the client sends a GET request to this endpoint, the response should contain the current status of the request. Кроме того, он также может включать предполагаемое время выполнения или ссылку для отмены операции. Optionally, it could also include an estimated time to completion or a link to cancel the operation.

Если асинхронная операция создает новый ресурс, конечная точка состояния должна вернуть код состояния 303 (см. другие) после завершения операции. If the asynchronous operation creates a new resource, the status endpoint should return status code 303 (See Other) after the operation completes. В ответе 303 включите заголовок Location, который предоставляет URI нового ресурса: In the 303 response, include a Location header that gives the URI of the new resource:

Фильтрация и разбитие данных на страницы Filter and paginate data

Предоставление коллекции ресурсов через один универсальный код ресурса (URI) может привести к тому, что приложения будут получать крупные объемы данных, когда нужно лишь подмножество информации. Exposing a collection of resources through a single URI can lead to applications fetching large amounts of data when only a subset of the information is required. Предположим, клиентскому приложению необходимо найти все заказы с суммой выше заданного значения. For example, suppose a client application needs to find all orders with a cost over a specific value. Он может получить все заказы через универсальный код ресурса (URI) /orders, а затем отфильтровать эти заказы на стороне клиента. It might retrieve all orders from the /orders URI and then filter these orders on the client side. Очевидно, что этот процесс крайне неэффективен. Clearly this process is highly inefficient. Он впустую использует пропускную способность сети и вычислительные ресурсы сервера, на котором размещен веб-API. It wastes network bandwidth and processing power on the server hosting the web API.

Вместо этого API может разрешить передачу фильтра в строке запроса URI, например /orders?minCost=n. Instead, the API can allow passing a filter in the query string of the URI, such as /orders?minCost=n. Веб-API отвечает за синтаксический анализ и обработку параметра minCost в строке запроса и возврат отфильтрованных результатов на стороне сервера. The web API is then responsible for parsing and handling the minCost parameter in the query string and returning the filtered results on the server side.

Запросы GET по ресурсам коллекций потенциально могут возвращать большое число элементов. GET requests over collection resources can potentially return a large number of items. При проектировании веб-API следует ввести ограничение на объем данных, возвращаемый одним запросом. You should design a web API to limit the amount of data returned by any single request. Рассмотрите возможность использования строк запроса, определяющих максимальное количество элементов для получения и начальное смещение в коллекции. Consider supporting query strings that specify the maximum number of items to retrieve and a starting offset into the collection. Пример. For example:

Также рассмотрите возможность наложения верхнего предела на количество возвращаемых элементов, чтобы предотвратить атаки типа «отказ в обслуживании». Also consider imposing an upper limit on the number of items returned, to help prevent Denial of Service attacks. Для поддержки клиентских приложений запросы GET, возвращающие разбитые по страницам данные, должны также включать какие-либо метаданные, указывающие общее число ресурсов, доступных в коллекции. To assist client applications, GET requests that return paginated data should also include some form of metadata that indicate the total number of resources available in the collection.

Аналогичную стратегию для сортировки данных можно также применить при их получении. Вы можете указать параметр сортировки, использующий имя поля в качестве значения, например /orders?sort=ProductID. You can use a similar strategy to sort data as it is fetched, by prov > Однако такой подход может негативно отразиться на кэшировании (так как параметры строки запроса составляют часть идентификатора ресурса, используемого многими реализациями кэша в качестве ключа к кэшированным данным). However, this approach can have a negative effect on caching, because query string parameters form part of the resource identifier used by many cache implementations as the key to cached data.

Вы можете расширить этот подход и ограничить возвращаемые поля для каждого элемента, если каждый элемент содержит большой объем данных. You can extend this approach to limit the fields returned for each item, if each item contains a large amount of data. Например, используйте параметр строки запроса, принимающий разделенный запятыми список полей, например /orders?fields=ProductID,Quantity. For example, you could use a query string parameter that accepts a comma-delimited list of fields, such as /orders?fields=ProductID,Quantity.

Присвойте содержательные значения по умолчанию всем необязательным параметрам в строках запроса. Give all optional parameters in query strings meaningful defaults. Например, установите параметру limit значение 10, а параметру offset — 0, если вы реализуете разбиение по страницам, параметру сортировки в качестве значения задайте ключ ресурса, если вы реализуете упорядочение, а в параметре fields укажите все поля в ресурсе при поддержке проекций. For example, set the limit parameter to 10 and the offset parameter to 0 if you implement pagination, set the sort parameter to the key of the resource if you implement ordering, and set the fields parameter to all fields in the resource if you support projections.

Поддержка частичных ответов для больших двоичных ресурсов Support partial responses for large binary resources

Ресурс может содержать большие двоичные поля, такие как файлы или изображения. A resource may contain large binary fields, such as files or images. Чтобы преодолеть проблемы, вызванные ненадежными и прерывистыми соединениями, а также улучшить время отклика, можно реализовать получение таких ресурсов пофрагментно. To overcome problems caused by unreliable and intermittent connections and to improve response times, consider enabling such resources to be retrieved in chunks. Для этого веб-API должен поддерживать заголовок Accept-Ranges для запросов GET по большим ресурсам. To do this, the web API should support the Accept-Ranges header for GET requests for large resources. Этот заголовок указывает, что операция GET поддерживает частичные запросы. This header indicates that the GET operation supports partial requests. Клиентское приложение может отправлять запросы GET, которые возвращают подмножество ресурса, указанное в виде диапазона байтов. The client application can submit GET requests that return a subset of a resource, specified as a range of bytes.

Кроме того, рассмотрите возможность применения HTTP-запросов HEAD для этих ресурсов. Also, consider implementing HTTP HEAD requests for these resources. Запрос HEAD аналогичен запросу GET с тем исключением, что он возвращает только заголовок HTTP, описывающий ресурс, и пустое сообщение. A HEAD request is similar to a GET request, except that it only returns the HTTP headers that describe the resource, with an empty message body. Клиентское приложение может отправить запрос HEAD, чтобы определить необходимость получения ресурса с помощью частичных запросов GET. A client application can issue a HEAD request to determine whether to fetch a resource by using partial GET requests. Пример. For example:

Вот пример сообщения ответа: Here is an example response message:

Заголовок Content-Length содержит общий размер ресурса, а заголовок Accept-Ranges указывает, что соответствующая операция GET поддерживает частичные результаты. The Content-Length header gives the total size of the resource, and the Accept-Ranges header indicates that the corresponding GET operation supports partial results. Клиентское приложение может использовать эту информацию для получения изображения небольшими фрагментами. The client application can use this information to retrieve the image in smaller chunks. Первый запрос возвращает первые 2500 байт с помощью заголовка «Range»: The first request fetches the first 2500 bytes by using the Range header:

Ответное сообщение указывает, что это частичный ответ, возвращая код состояния HTTP 206. The response message indicates that this is a partial response by returning HTTP status code 206. Заголовок «Content-Length» указывает фактическое число возвращаемых байтов в тексте сообщения (не размер ресурса), а заголовок «Content-Range» указывает, какая это часть ресурса (байты 0–2499 из 4580): The Content-Length header specifies the actual number of bytes returned in the message body (not the size of the resource), and the Content-Range header indicates which part of the resource this is (bytes 0-2499 out of 4580):

Последующий запрос от клиентского приложения может получить оставшуюся часть ресурса. A subsequent request from the client application can retrieve the remainder of the resource.

Использование подхода HATEOAS для обеспечения возможности перехода к связанным ресурсам Use HATEOAS to enable navigation to related resources

Одна из основных целей реализации модели REST — получение возможности перемещаться внутри всего набора ресурсов без предварительного знания схемы универсальных кодов ресурсов (URI). One of the primary motivations behind REST is that it should be possible to navigate the entire set of resources without requiring prior knowledge of the URI scheme. Каждый HTTP-запрос GET должен возвращать сведения, необходимые для поиска ресурсов, напрямую связанных с запрашиваемым объектом, посредством гиперссылок, включенных в ответ. Запросу GET также необходимо предоставить сведения, описывающие операции, доступные в каждом из этих ресурсов. Each HTTP GET request should return the information necessary to find the resources related directly to the requested object through hyperlinks included in the response, and it should also be provided with information that describes the operations available on each of these resources. Этот принцип называется HATEOAS (гипертекст как обработчик состояния приложения). This principle is known as HATEOAS, or Hypertext as the Engine of Application State. Система фактически представляет собой конечный автомат. Ответ по каждому запросу содержит сведения, необходимые для перемещения между состояниями. Другие сведения не требуются. The system is effectively a finite state machine, and the response to each request contains the information necessary to move from one state to another; no other information should be necessary.

На сегодняшний день не существует стандартов или спецификаций, определяющих правила моделирования принципа HATEOAS. Currently there are no standards or specifications that define how to model the HATEOAS principle. Примеры в этом разделе демонстрируют одно из возможных решений. The examples shown in this section illustrate one possible solution.

Например, чтобы обрабатывать связь между заказом и клиентом, представление заказа может включать ссылки, которые идентифицируют доступные операции для отправителя заказа. For example, to handle the relationship between an order and a customer, the representation of an order could include links that identify the available operations for the customer of the order. Вот возможное представление: Here is a possible representation:

В этом примере в массиве links есть набор ссылок. In this example, the links array has a set of links. Каждая ссылка представляет операцию в связанной сущности. Each link represents an operation on a related entity. Данные для каждой ссылки включают связь («customer»), URI ( https://adventure-works.com/customers/3 ), метод HTTP и поддерживаемые типы MIME. The data for each link includes the relationship («customer»), the URI ( https://adventure-works.com/customers/3 ), the HTTP method, and the supported MIME types. Это все данные, необходимые клиентскому приложению, чтобы иметь возможность вызова операции. This is all the information that a client application needs to be able to invoke the operation.

Массив links также включает автореферентные сведения, относящиеся к полученному ресурсу. The links array also includes self-referencing information about the resource itself that has been retrieved. Они имеют отношение self. These have the relationship self.

Набор возвращаемых ссылок может изменяться в зависимости от состояния ресурса. The set of links that are returned may change, depending on the state of the resource. Вот почему гипертекст является «обработчиком состояния приложения». This is what is meant by hypertext being the «engine of application state.»

Управление версиями веб-API RESTful Versioning a RESTful web API

Маловероятно, что веб-API останется статичным. It is highly unlikely that a web API will remain static. По мере изменения бизнес-требований добавляются новые коллекции ресурсов, изменяются связи между ресурсами и структура данных в ресурсах. As business requirements change new collections of resources may be added, the relationships between resources might change, and the structure of the data in resources might be amended. Несмотря на то что обновление веб-API для соответствия новым или измененным требованиям — довольно простой процесс, вам необходимо учесть, какое воздействие эти изменения окажут на клиентские приложения, использующие веб-API. While updating a web API to handle new or differing requirements is a relatively straightforward process, you must consider the effects that such changes will have on client applications consuming the web API. Причина в том, что хотя разработка и реализация веб-API имеет полный контроль над этим API, разработчик не имеет такой же уровень контроля над клиентскими приложениями, который может быть создан сторонними организациями, работающими удаленно. The issue is that although the developer designing and implementing a web API has full control over that API, the developer does not have the same degree of control over client applications, which may be built by third-party organizations operating remotely. В первую очередь необходимо позволить существующим клиентским приложениям продолжать работу без изменений, при этом предоставляя новым клиентским приложениям доступ к новым функциям и ресурсам. The primary imperative is to enable existing client applications to continue functioning unchanged while allowing new client applications to take advantage of new features and resources.

Управление версиями позволяет веб-API указывать предоставляемые функции и ресурсы, за счет чего клиентское приложение может отправлять запросы для определенной версии функции или ресурса. Versioning enables a web API to indicate the features and resources that it exposes, and a client application can submit requests that are directed to a specific version of a feature or resource. В следующих разделах описано несколько различных подходов со своими преимуществами и недостатками. The following sections describe several different approaches, each of which has its own benefits and trade-offs.

Отсутствие управления версиями No versioning

Эта простейший подход, приемлемый для некоторых внутренних API. This is the simplest approach, and may be acceptable for some internal APIs. Значительные изменения можно представить в виде новых ресурсов или новых ссылок. Significant changes could be represented as new resources or new links. Добавление содержимого в существующие ресурсы может не представлять критические изменения, так как клиентские приложения, не ожидающие просмотра этого содержимого, будут игнорировать его. Adding content to existing resources might not present a breaking change as client applications that are not expecting to see this content will ignore it.

Например, запрос на универсальный код ресурса (URI) https://adventure-works.com/customers/3 должен вернуть сведения одного клиента с полями id , name и address , которые ожидает клиентское приложение. For example, a request to the URI https://adventure-works.com/customers/3 should return the details of a single customer containing id , name , and address fields expected by the client application:

Для простоты примеры ответов в этом разделе не содержат ссылок HATEOAS. For simplicity, the example responses shown in this section do not include HATEOAS links.

Если поле DateCreated добавить в схему ресурса клиента, ответ будет выглядеть следующим образом: If the DateCreated field is added to the schema of the customer resource, then the response would look like this:

Существующие клиентские приложения могут продолжить работать без ошибок, если они могут пропускать нераспознанные поля, в то время как при проектировании новых клиентских приложений можно обеспечить поддержку новых полей. Existing client applications might continue functioning correctly if they are capable of ignoring unrecognized fields, while new client applications can be designed to handle this new field. Однако внесение более серьезных изменений в схему ресурсов (таких как удаление или переименование полей) или изменение связей между ресурсами могут быть значительными изменениями, мешающими корректной работе существующих клиентских приложений. However, if more radical changes to the schema of resources occur (such as removing or renaming fields) or the relationships between resources change then these may constitute breaking changes that prevent existing client applications from functioning correctly. В таких ситуациях следует рассмотреть один из следующих подходов. In these situations, you should consider one of the following approaches.

Управление версиями через универсальные коды ресурсов (URI) URI versioning

При каждом изменении веб-API или схемы ресурсов вы добавляете номер версии в универсальный код (URI) каждого ресурса. Each time you modify the web API or change the schema of resources, you add a version number to the URI for each resource. Уже существующие универсальные коды ресурсов (URI) должны продолжить функционировать без изменений, возвращая ресурсы, соответствующие их исходной схеме. The previously existing URIs should continue to operate as before, returning resources that conform to their original schema.

Расширение предыдущего примера, если поле address переструктурировано в подполя, содержащие каждую составляющую часть адреса (например, streetAddress , city , state и zipCode ), эта версия ресурса может быть предоставлена с помощью URI, содержащего номер версии, например https://adventure-works.com/v2/customers/3 : Extending the previous example, if the address field is restructured into subfields containing each constituent part of the address (such as streetAddress , city , state , and zipCode ), this version of the resource could be exposed through a URI containing a version number, such as https://adventure-works.com/v2/customers/3 :

Этот механизм управления версиями очень прост, но для него требуется, чтобы сервер направлял запрос к соответствующей конечной точке. This versioning mechanism is very simple but depends on the server routing the request to the appropriate endpoint. Однако этот подход может стать чрезмерно сложным по мере прохождения веб-API через несколько итераций и необходимости поддержки сервером нескольких различных версий. However, it can become unwieldy as the web API matures through several iterations and the server has to support a number of different versions. Кроме того, c пуристической точки зрения, во всех случаях клиентские приложения получают одни и те же данные (клиент 3), поэтому универсальный код ресурса (URI) фактически не должен отличаться в зависимости от версии. Also, from a purist’s point of view, in all cases the client applications are fetching the same data (customer 3), so the URI should not really be different depending on the version. Эта схема также усложняет реализацию HATEOAS, поскольку потребуется включать номер версии в универсальные коды ресурсов (URI) всех ссылок. This scheme also complicates implementation of HATEOAS as all links will need to include the version number in their URIs.

Управление версиями через строку запроса Query string versioning

Чтобы не указывать множество кодов URI, вы можете указать версию ресурса с помощью параметра в строке запроса, добавленного к HTTP-запросу, например https://adventure-works.com/customers/3?version=2 . Rather than providing multiple URIs, you can specify the version of the resource by using a parameter within the query string appended to the HTTP request, such as https://adventure-works.com/customers/3?version=2 . Значение по умолчанию параметра версии должно быть содержательным, например 1, если оно опускается клиентскими приложениями прежних версий. The version parameter should default to a meaningful value such as 1 if it is omitted by older client applications.

Этот подход обладает семантическим преимуществом в том плане, что один и тот же ресурс всегда возвращается по одинаковому универсальному коду ресурса (URI). Однако он зависит от кода, обрабатывающего запрос для анализа строки запроса и возврата соответствующего HTTP-ответа. This approach has the semantic advantage that the same resource is always retrieved from the same URI, but it depends on the code that handles the request to parse the query string and send back the appropriate HTTP response. Кроме того, этот подход также усложняет реализацию HATEOAS, как и механизм управления версиями через универсальные коды ресурсов (URI). This approach also suffers from the same complications for implementing HATEOAS as the URI versioning mechanism.

Некоторые браузеры прежних версий и веб-прокси не могут кэшировать ответы по запросам, содержащим строку запроса в URL-адресе. Some older web browsers and web proxies will not cache responses for requests that include a query string in the URI. Это может привести к снижению производительности веб-приложений, использующих веб-API и запускаемых из такого веб-браузера. This can degrade performance for web applications that use a web API and that run from within such a web browser.

Управление версиями через заголовок Header versioning

Вместо того чтобы добавлять номер версии в виде параметра строки запроса, вы можете реализовать пользовательский заголовок, указывающий версию ресурса. Rather than appending the version number as a query string parameter, you could implement a custom header that indicates the version of the resource. Этот подход требует от клиентского приложения добавления соответствующего заголовка во все запросы. При этом в коде, обрабатывающем запрос клиентского приложения, можно использовать значение по умолчанию (версия 1), если заголовок с номером версии опускается. This approach requires that the client application adds the appropriate header to any requests, although the code handling the client request could use a default value (version 1) if the version header is omitted. В следующих примерах используется пользовательский заголовок с именем Custom-Header. The following examples use a custom header named Custom-Header. Значение этого заголовка указывает версию веб-API. The value of this header indicates the version of web API.

Версия 1: Version 1:

Версия 2: Version 2:

Как и в предыдущих двух подходах, реализация HATEOAS требует включения соответствующего пользовательского заголовка в любые ссылки. As with the previous two approaches, implementing HATEOAS requires including the appropriate custom header in any links.

Управление версиями через тип носителя Media type versioning

При отправке HTTP-запроса GET на веб-сервер клиентское приложение должно указывать поддерживаемый формат содержимого с помощью заголовка «Accept», как описано ранее в этом руководстве. When a client application sends an HTTP GET request to a web server it should stipulate the format of the content that it can handle by using an Accept header, as described earlier in this guidance. Нередко заголовок Accept используется для того, чтобы позволить клиентскому приложению указать требуемый формат текста ответа: XML, JSON или другой формат, который клиентское приложение может анализировать. Frequently the purpose of the Accept header is to allow the client application to specify whether the body of the response should be XML, JSON, or some other common format that the client can parse. Однако можно определить пользовательские типы носителей, которые содержат сведения, позволяющие клиентскому приложению указывать предпочтительную версию ресурса. However, it is possible to define custom media types that include information enabling the client application to indicate which version of a resource it is expecting. В следующем примере показан запрос, в котором используется заголовок Accept со значением application/vnd.adventure-works.v1+json. The following example shows a request that specifies an Accept header with the value application/vnd.adventure-works.v1+json. Элемент vnd.adventure-works.v1 указывает веб-серверу на необходимость возврата ресурса версии 1, в то время как элемент json указывает JSON в качестве предпочтительного формата текста ответа. The vnd.adventure-works.v1 element indicates to the web server that it should return version 1 of the resource, while the json element specifies that the format of the response body should be JSON:

Код, обрабатывающий запрос, отвечает за обработку заголовка Accept и максимальное выполнение содержащихся в нем требований (клиентское приложение может указать в заголовке Accept несколько форматов, в случае чего веб-сервер может выбрать наиболее подходящий формат текста ответа). The code handling the request is responsible for processing the Accept header and honoring it as far as possible (the client application may specify multiple formats in the Accept header, in which case the web server can choose the most appropriate format for the response body). Веб-сервер подтверждает формат данных в тексте ответа с помощью заголовка «Content-Type»: The web server confirms the format of the data in the response body by using the Content-Type header:

Если в заголовке «Accept» не указан ни один из известных типов носителя, веб-сервер может отправить ответное сообщение с кодом HTTP 406 (неприемлемо) или вернуть сообщение с типом носителя по умолчанию. If the Accept header does not specify any known media types, the web server could generate an HTTP 406 (Not Acceptable) response message or return a message with a default media type.

Вероятно, это самый полноценный механизм управления версиями, беспрепятственно поддерживающий принцип HATEOAS, который может включать MIME-тип связанных данных в ссылках ресурсов. This approach is arguably the purest of the versioning mechanisms and lends itself naturally to HATEOAS, which can include the MIME type of related data in resource links.

При выборе стратегии управления версиями вам также следует учесть воздействие на производительность, особенно при кэшировании на веб-сервере. When you select a versioning strategy, you should also consider the implications on performance, especially caching on the web server. Управление версиями через универсальные коды ресурсов (URI) и строку запроса поддерживает кэширование, поскольку одинаковое сочетание универсального кода ресурса (URI) и строки запроса каждый раз соотносится с одними и теми же данными. The URI versioning and Query String versioning schemes are cache-friendly inasmuch as the same URI/query string combination refers to the same data each time.

Механизмы управления версиями через заголовок и тип носителя, как правило, требуют дополнительной логики для проверки значений в пользовательском заголовке или заголовке «Accept». The Header versioning and Media Type versioning mechanisms typically require additional logic to examine the values in the custom header or the Accept header. Использование многочисленными клиентами разных версий веб-API в крупномасштабной среде может привести к образованию большого объема повторяющихся данных в кэше на стороне сервера. In a large-scale environment, many clients using different versions of a web API can result in a significant amount of duplicated data in a server-side cache. Эта проблема может усложниться, если клиентское приложение обменивается данными с веб-сервером через прокси-сервер, реализующий кэширование, который перенаправляет запрос на веб-сервер лишь в том случае, если в его кэше на текущий момент не содержится копия запрашиваемых данных. This issue can become acute if a client application communicates with a web server through a proxy that implements caching, and that only forwards a request to the web server if it does not currently hold a copy of the requested data in its cache.

Open API Initiative Open API Initiative

Организация Open API Initiative создана отраслевым консорциумом для стандартизации описаний REST API разных поставщиков. The Open API Initiative was created by an industry consortium to standardize REST API descriptions across vendors. В рамках этой инициативы спецификация Swagger 2.0 была переименована в OpenAPI Specification (OAS) и перенесена в проект Open API Initiative. As part of this initiative, the Swagger 2.0 specification was renamed the OpenAPI Specification (OAS) and brought under the Open API Initiative.

Возможно, вам потребуется применить OpenAPI для вашего веб-API. You may want to adopt OpenAPI for your web APIs. Учитывайте следующие факторы. Some points to consider:

Спецификация OpenAPI поставляется с набором заключений и рекомендаций по разработке REST API. The OpenAPI Specification comes with a set of opinionated guidelines on how a REST API should be designed. Она дает ряд преимуществ для взаимодействия, но требует дополнительного внимания при разработке API для соблюдения спецификации. That has advantages for interoperability, but requires more care when designing your API to conform to the specification.

В OpenAPI рекомендуется начинать работу с создания контракта, а не реализации. OpenAPI promotes a contract-first approach, rather than an implementation-first approach. Это означает, что при разработке API вы сначала создаете контракт (интерфейс) и лишь после этого пишете код для его реализации. Contract-first means you design the API contract (the interface) first and then write code that implements the contract.


При помощи Swagger и других средства можно создавать клиентские библиотеки и документацию на основе контрактов API. Tools like Swagger can generate client libraries or documentation from API contracts. Например, см. веб-API ASP.NET страниц справки с помощью Swagger. For example, see ASP.NET Web API help pages using Swagger.

Дополнительные сведения More information

Рекомендации корпорации майкрософт REST API. Microsoft REST API guidelines. Подробные рекомендации по разработке общедоступных REST API. Detailed recommendations for designing public REST APIs.

Контрольный список веб-API. Web API checklist. Список факторов, которые следует учитывать при разработке и реализации веб-API. A useful list of items to consider when designing and implementing a web API.

Open API Initiative. Open API Initiative. Документация и сведения о реализации Open API. Documentation and implementation details on Open API.

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

REST (сокр. от англ. Representational State Transfer-«передача состояния представления»)— архитектурный стиль взаимодействия компонентов распределённого приложения в сети. REST представляет собой согласованный набор ограничений, учитываемых при проектировании распределённой гипермедиа-системы. В определённых случаях (интернет-магазины, поисковые системы, прочие системы, основанные на данных) это приводит к повышению производительности и упрощению архитектуры. В широком смысле[уточнить] компоненты в REST взаимодействуют наподобие взаимодействия клиентов и серверов во Всемирной паутине. REST является альтернативой RPC group: [Источник 1]

В сети Интернет вызов удалённой процедуры может представлять собой обычный HTTP-запрос (обычно «GET» или «POST»; такой запрос называют «REST-запрос»), а необходимые данные передаются в качестве параметров запроса group: [Источник 2] group: [Источник 3]

Для веб-служб, построенных с учётом REST (то есть не нарушающих накладываемых им ограничений), применяют термин «RESTful».

В отличие от веб-сервисов (веб-служб) на основе SOAP, не существует «официального» стандарта для RESTful веб-API. Дело в том, что REST является архитектурным стилем, в то время как SOAP является протоколом. Несмотря на то, что REST не является стандартом сам по себе, большинство RESTful-реализаций используют стандарты, такие как HTTP, URL, JSON и XML.

Содержание

История термина

Хотя данная концепция лежит в самой основе Всемирной паутины — термин «REST» был введён Роем Филдингом, одним из создателей протокола «HTTP», лишь в 2000 году. В своей диссертации «Архитектурные стили и дизайн сетевых программных архитектур» («Architectural Styles and the Design of Network-based Software Architectures»)group: [Источник 4]

Стиль «REST» развивался параллельно с «HTTP 1.1», разработанным в 1996—1999 годах, основываясь на существующем дизайне «HTTP 1.0», разработанном в 1996 году group: [Источник 5]

Свойства Архитектуры REST

Свойства архитектуры, которые зависят от ограничений, наложенных на REST-системы:

  • Производительность — взаимодействие компонентов системы может являться доминирующим фактором производительности и эффективности сети с точки зрения пользователя
  • Масштабируемость для обеспечения большого числа компонентов и взаимодействий компонентов.

Рой Филдинг — один из главных авторов спецификации протокола HTTP, описывает влияние архитектуры REST на масштабируемость следующим образом:

  • Простота унифицированного интерфейса
  • Открытость компонентов к возможным изменениям для удовлетворения изменяющихся потребностей (даже при работающем приложении)
  • Прозрачность связей между компонентами системы для сервисных служб
  • Переносимость компонентов системы путем перемещения программного кода вместе с данными
  • Надежность является устойчивостью к отказам на уровне системы при наличии отказов отдельных компонентов, соединений, или данных

Требования к архитектуре REST

Существует шесть обязательных ограничений для построения распределённых REST-приложений по Филдингу group: [Источник 6]

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

Если сервис-приложение нарушает любое из этих ограничительных условий, данную систему нельзя считать REST-системой.

Обязательными условиями-ограничениями являются:

Модель клиент-сервер

Первым ограничением применимым к нашей гибридной модели является приведение архитектуры к модели клиент-сервер, описанной в параграфе 3.4.1. Разграничение потребностей является принципом, лежащим в основе данного накладываемого ограничения. Отделение потребности интерфейса клиента от потребностей сервера, хранящего данные, повышает переносимость кода клиентского интерфейса на другие платформы, а упрощение серверной части улучшает масштабируемость. Наибольшее же влияние на всемирную паутину, пожалуй, имеет само разграничение, которое позволяет отдельным частям развиваться независимо друг от друга, поддерживая потребности в развитии интернета со стороны различных организаций.

Отсутствие состояния

Протокол взаимодействия между клиентом и сервером требует соблюдения следующего условия: в период между запросами клиента никакая информация о состоянии клиента на сервере не хранится. (Stateless protocol (англ.)русск.. Все запросы от клиента должны быть составлены так, чтобы сервер получил всю необходимую информацию для выполнения запроса. Состояние сессии при этом сохраняется на стороне клиента. Информация о состоянии сессии может быть передана сервером какому-либо другому сервису (например в службу базы данных) для поддержания устойчивого состояния, например с целью, и на период установления аутентификации. Клиент инициирует отправку запросов, когда он готов (возникает необходимость) перейти в новое состояние.

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

Кэширование

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

Единообразие интерфейса

Наличие унифицированного интерфейса являются фундаментальным требованием дизайна REST-сервисов. Унифицированные интерфейсы позволяют каждому из сервисов развиваться независимо.

К унифицированным интерфейсам предъявляются следующие четыре ограничительных условия:

  • Все ресурсы идентифицируются в запросах, например, с использованием URI в интернет-системах. Ресурсы концептуально отделены от представлений, которые возвращаются клиентам. Например, сервер может отсылать данные из базы данных в виде HTML, XML или JSON, ни один из которых не является типом хранения внутри сервера.

Манипуляция ресурсами через представление

  • Если клиент хранит представление ресурса, включая метаданные — он обладает достаточной информацией для модификации или удаления ресурса.
  • Каждое сообщение содержит достаточно информации, чтобы понять каким образом его обрабатывать. К примеру, обработчик сообщения (parser) необходимый для извлечения данных может быть указан в списке MIME-типов.

Гипермедиа, как средство изменения состояния приложения

  • Клиенты изменяют состояние системы только через действия, которые динамически определены в гипермедиа на сервере (к примеру, гиперссылки в гипертексте). Исключая простые точки входа в приложение, клиент не может предположить что доступна какая-то операция над каким-то ресурсом, если не получил информацию об этом в предыдущих запросах к серверу. Не существует универсального формата для предоставления ссылок между ресурсами, RFC 5988 и JSON Hypermedia API Language являются 2мя популярными форматами предоставления ссылок в REST HYPERMEDIA сервисах.

Клиент обычно не способен точно определить взаимодействует ли он напрямую с сервером, или же с промежуточным узлом в связи иерархической структурой сетей (слои). Применение промежуточных серверов способно повысить масштабируемость за счет балансировки нагрузки и распределенного кэширования. Промежуточные узлы также могут подчиняться политике безопасности с целью обеспечению конфиденциальности информации [Источник 7] .

Код по требованию (необязательное ограничение)

REST может позволить расширить функциональность клиента за счёт загрузки кода с сервера в виде апплетов или сценариев. Филдинг утверждает, что дополнительное ограничение позволяет проектировать архитектуру, поддерживающую желаемую функциональность в общем случае, но возможно за исключением некоторых контекстов.

Преимущества

Филдинг указывал, что приложения, не соответствующие приведённым условиям, не могут называться REST-приложениями. Если же все условия соблюдены, то, по его мнению, приложение получит следующие преимущества:

  • Надёжность (за счёт отсутствия необходимости сохранять информацию о состоянии клиента, которая может быть утеряна);
  • Производительность (за счёт использования кэша);
  • Масштабируемость;
  • Прозрачность системы взаимодействия (особенно необходимая для приложений обслуживания сети);
  • Простота интерфейсов;
  • Портативность компонентов;
  • Лёгкость внесения изменений;
  • Способность эволюционировать, приспосабливаясь к новым требованиям (на примере Всемирной паутины).

Web-сервисы RESTful

Основы

REST определяет ряд архитектурных принципов проектирования Web-сервисов, ориентированных на системные ресурсы, включая способы обработки и передачи состояний ресурсов по HTTP разнообразными клиентскими приложениями, написанными на различных языках программирования. За последние несколько лет REST стала преобладающей моделью проектирования Web-сервисов. Фактически REST оказала настолько большое влияние на Web, что практически вытеснила дизайн интерфейса, основанный на SOAP и WSDL, из-за значительного более простого стиля проектирования.

Технология REST не привлекла большого внимания в 2000 году, когда Рой Филдинг (Roy Fielding) впервые представил ее в Калифорнийском университете в Ирвайне в своей диссертации «Архитектурные стили и дизайн сетевых архитектур программного обеспечения», где анализировался набор принципов архитектуры программного обеспечения, использующей Web в качестве платформы распределенных вычислений. Однако сегодня, по прошествии многих лет, возникли и продолжают развиваться многочисленные инфраструктуры для REST, которую, среди прочего, планируется интегрировать в стандарт Java™ 6 JSR-311.

Конкретная реализация Web-сервисов REST следует четырем базовым принципам проектирования:

  • Явное использование HTTP-методов.
  • Несохранение состояния.
  • Предоставление URI, аналогичных структуре каталогов.
  • Передача данных в XML, JavaScript Object Notation (JSON) или в обоих форматах.

Явное использование HTTP-методов

Одной из ключевых характеристик Web-сервиса RESTful является явное использование HTTP-методов согласно протоколу, определенному в RFC 2616. Например, HTTP GET определяется как метод генерирования данных, используемый клиентским приложением для извлечения ресурса, получения данных с Web-сервера или выполнения запроса в надежде на то, что Web-сервер найдет и возвратит набор соответствующих ресурсов.

REST предлагает разработчикам использовать HTTP-методы явно в соответствии с определением протокола. Этот основной принцип проектирования REST устанавливает однозначное соответствие между операциями create, read, update и delete (CRUD) и HTTP-методами. Согласно этому соответствию:

  • Для создания ресурса на сервере используется POST.
  • Для извлечения ресурса используется GET.
  • Для изменения состояния ресурса или его обновления используется PUT.
  • Для удаления ресурса используется DELETE.

Недостатком проектирования многих Web API является использование HTTP-методов не по прямому назначению. Например, URI запроса в HTTP GET обычно определяет один конкретный ресурс, либо же строка запроса в URI запроса содержит ряд параметров, определяющих критерии поиска сервером набора соответствующих ресурсов. По крайней мере именно так описан метод GET в HTTP/1.1 RFC. Однако часто встречаются непривлекательные Web API, использующие HTTP GET для выполнения разного рода транзакций на сервере (например, для добавления записей в базу данных). В таких случаях URI запроса GET используется некорректно или, по крайней мере, не используется в REST-стиле (RESTfully). Если Web API использует GET для запуска удаленных процедур, запрос может выглядеть примерно так:

Это неудачный дизайн, поскольку вышеупомянутый Web-метод поддерживает меняющую состояние операцию посредством HTTP-запроса GET. Иначе говоря, HTTP-запрос GET имеет побочные эффекты. В случае успешного выполнения запроса в хранилище данных будет добавлен новый пользователь (в нашем примере – Robert). Проблема здесь в основном семантическая. Web-серверы предназначены для ответов на HTTP-запросы GET путем извлечения ресурсов согласно URI запроса (или критерию запроса) и возврата их или их представления в ответе, а не для добавления записи в базу данных. С точки зрения предполагаемого использования и с точки зрения HTTP/1.1-совместимых Web-серверов такое использование GET является ненадлежащим.

Кроме семантики еще одной проблемой является то, что для удаления, изменения или добавления записи в базу данных или для изменения каким-либо образом состояния на стороне сервера GET привлекает различные средства Web-кэширования (роботы) и поисковые механизмы, которые могут выполнять непреднамеренные изменения на сервере путем простого обхода ссылки. Простым способом решения этой общей проблемы является помещение имен и значений параметров URI запроса в XML-теги. Эти теги (XML-представление создаваемого объекта) можно отправить в теле HTTP-запроса POST, URI которого является родителем объекта (см.образец RESTful-запроса 1(До) и 2(После)). образец 1(До)

Это образец RESTful-запроса: HTTP-запрос POST используется корректно, а тело запроса содержит полезную нагрузку. На принимающей стороне в запрос может быть добавлен содержащийся в теле ресурс, подчиненный ресурсу, определенному в URI запроса; в данном случае новый ресурс должен добавляться как потомок /users. Такое отношение включения (containment) между новым логическим объектом и его родителем, указанное в запросе POST, аналогично отношению подчинения между файлом и родительским каталогом. Клиентское приложение устанавливает отношение между логическим объектом и его родителем и определяет URI нового объекта в запросе POST.

Затем клиентское приложение может получить представление ресурса, используя новый URI, указывающий, что по крайней мере логически ресурс расположен в /users (см. образец 3). образец 3. HTTP-запрос GET

Это правильное применение запроса GET, поскольку он служит только для извлечения данных. GET – это операция, которая должна быть свободной от побочных эффектов. Данное свойство известно также под названием идемпотентность.

Аналогичный рефакторинг Web-метода необходимо выполнить в тех ситуациях, когда HTTP-запрос GET поддерживает операцию update (см. образец 4). образец 4. Операция update в HTTP-запросе GET

Это запрос меняет атрибут (или свойство) name ресурса. Хотя для такой операции можно использовать строку запроса и в листинге 4 приведена самая простая из них, модель «строка запроса как сигнатура метода» не работает для более сложных операций. Поскольку нашей целью является явное использование HTTP-методов, по указанным выше причинам более RESTful-совместимым является подход, при котором для обновления ресурса применяется HTTP-запрос PUT вместо HTTP-запроса GET (см. образец 5). образец 5. HTTP-запрос PUT

Использование запроса PUT для замены исходного ресурса обеспечивает гораздо более прозрачный интерфейс, совместимый с принципами REST и с определением HTTP-методов. Запрос PUT в листинге 5 является явным в том смысле, что он указывает на обновляемый ресурс, определяя его в URI запроса, и передает новое представление ресурса от клиента на сервер в теле запроса PUT, вместо того чтобы передавать атрибуты ресурса в виде слабо связанного набора имен и значений параметров в URI запроса. Запрос в листинге 5 переименовывает ресурс с Robert на Bob и меняет его URI на /users/Bob. В Web-сервисе REST использование старого URI в последующих запросах ресурса приведет к возникновению стандартной ошибки 404 Not Found.

Общепринятым подходом, соответствующим рекомендациям REST по явному применению HTTP-методов, является использование в URI имен существительных вместо глаголов. В Web-сервисе RESTful глаголы POST, GET, PUT и DELETE уже определены протоколом. В идеале для реализации обобщенного интерфейса и явного вызова операций клиентскими приложениями Web-сервис не должен определять дополнительные команды или удаленные процедуры, например /adduser или /updateuser. Этот общий принцип применим также к телу HTTP-запроса, которое предназначено для передачи состояния ресурса, а не имени вызываемых удаленного метода или удаленной процедуры.


Несохранение состояния

Для удовлетворения постоянно растущих требований к производительности Web-сервисы REST должны быть масштабируемыми. Для формирования топологии сервисов, позволяющей при необходимости перенаправлять запросы с одного сервера на другой с целью уменьшения общего времени реакции на вызов Web-сервиса, обычно применяют кластеры серверов с возможностью распределения нагрузки и аварийного переключения на резерв, прокси-серверы и шлюзы. Использование промежуточных серверов для улучшения масштабируемости требует, чтобы клиенты Web-сервисов REST отправляли полные самодостаточные запросы, содержащие все необходимые для их выполнения данные, чтобы компоненты на промежуточных серверах могли перенаправлять, маршрутизировать и распределять нагрузку без локального сохранения состояния между запросами.

При обработке полного самодостаточного запроса серверу не нужно извлекать состояние или контекст приложения. Приложение (или клиент) Web-сервиса REST включает в HTTP-заголовки и в тело запроса все параметры, контекст и данные, необходимые серверному компоненту для генерирования ответа. В этом смысле несохранение состояния (statelessness) улучшает производительность Web-сервиса и упрощает дизайн и реализацию серверных компонентов, поскольку отсутствие состояния на сервере устраняет необходимость синхронизации сеансовых данных с внешним приложением.

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

Подобные сохраняющие состояние сервисы получаются сложными. На платформе Java EE (Java Platform, Enterprise Edition) сохраняющие состояние сервисы требуют большого количества предварительных соглашений по эффективному хранению и синхронизации сеансовых данных в кластере Java EE-контейнеров. В средах такого типа разработчики сервлетов/страниц JSP (JavaServer Pages) и EJB-компонентов (Enterprise JavaBeans) сталкиваются с типичной проблемой поиска причин возникновения исключительной ситуации java.io.NotSerializableException при репликации сеанса. Независимо от источника (контейнер сервлетов при репликации HttpSession или EJB-контейнер при репликации сохраняющего состояние EJB-компонента) такая проблема может стоить разработчикам нескольких дней поисков в сложном графе объектов, определяющих состояние сервера, того единственного объекта, в котором реализуется Serializable. Кроме того, синхронизация сеансов увеличивает накладные расходы, что отрицательно сказывается на производительности сервера.

В противоположность этому, не сохраняющие состояния серверные компоненты более просты в проектировании, написании и распределении между серверами со сбалансированной нагрузкой. Не сохраняющий состояния сервис не только лучше работает, но и возлагает основную ответственность за сохранение состояния на клиентское приложение. В Web-сервисе RESTful сервер отвечает за генерирование ответов и за предоставление интерфейса, позволяющего клиентскому приложению самому хранить свое состояние. Например, при запросе многостраничного набора результатов клиентское приложение должно включать в запрос номер конкретной страницы, а не просто запрашивать следующую страницу (см. рисунок 2).

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

Сервер

  • Генерирует ответы, содержащие ссылки на другие ресурсы для навигации приложений по связанным ресурсам. Такие ответы содержат встроенные ссылки. Аналогичным образом при запросе родительского или контейнерного ресурса типичный RESTful-ответ может содержать ссылки на потомков родительского элемента или на подчиненные ресурсы, чтобы сохранять связь с ними.
  • Генерирует ответы, содержащие информацию о том, подлежат ли они кэшированию с целью повышения производительности за счет уменьшения количества запросов дублирующихся ресурсов и полного отказа от некоторых запросов. Для этого сервер включает в ответ HTTP-заголовки Cache-Control и Last-Modified (значение данных).

Клиентское приложение

  • По заголовку Cache-Control ответа определяет возможность кэширования ресурса (его локального копирования). Также клиентское приложение читает заголовок Last-Modified ответа и возвращает значение даты в заголовке If-Modified-Since для отправки на сервер запроса об изменении ресурса. Такой запрос, т.н. Conditional GET, использует оба заголовка. Если ресурс с указанного времени не изменился, ответом сервера является стандартный код 304 (Not Modified) и запрошенный ресурс не отправляется. Код ответа 304 HTTP означает, что клиентское приложение может спокойно использовать кэшированную локальную копию представления ресурса в качестве самой последней его версии, фактически опуская последующие запросы GET до тех пор, пока ресурс не будет изменен.
  • Отправляет полные запросы, которые могут обрабатываться независимо от других запросов. Это требует от клиентского приложения использования в полном объеме HTTP-заголовков, определенных интерфейсом Web-сервиса, и отправки полных представлений ресурсов в теле запроса. Клиентское приложение отправляет запросы, которые практически ничего не знают о предшествующих запросах, о существовании сеанса на сервере, о способности сервера добавлять контекст в запрос и о состоянии приложения, сохраняющемся между запросами.

Такая совместная работа клиентского приложения и сервиса очень важна для отказа от сохранения состояния в Web-сервисах RESTful. Результатом ее является рост производительности за счет уменьшения трафика и минимизации состояния серверного приложения.

Отображение URI, аналогичных структуре каталогов

С точки зрения обращения к ресурсам из клиентского приложения предоставляемые URI определяют, насколько интуитивным будет Web-сервис REST и будет ли он использоваться так, как предполагал разработчик. Третья характеристика Web-сервиса RESTful полностью посвящена URI.

URI-адреса Web-сервиса REST должны быть интуитивно понятными. Рассматривайте URI как некий самодокументирующийся интерфейс, почти не требующий пояснения или обращения к разработчику для его понимания и для получения соответствующих ресурсов. Поэтому структура URI должна быть простой, предсказуемой и понятной.

Один из способов достичь такого уровня удобства использования – построение URI по аналогии со структурой каталогов. Такого рода URI являются иерархическими, исходящим из одного корневого пути, ветвления которого отображают основные функции сервиса. Согласно этому определению, URI – это не просто строка со слэшами в качестве разделителей, а скорее дерево с вышележащими и нижележащими ветвями, соединенными в узлах. Например, в сервисе обсуждений различных тем (от Java до бумаги) можно определить структурированный набор URI следующего вида:

Приведем некоторые дополнительные рекомендации, на которые следует обратить внимание при обдумывании структуры URI для Web-сервисов RESTful:

  • Скрывайте расширения файлов серверных сценариев (.jsp, .php, .asp), если таковые используются, чтобы можно было выполнить портирование приложений на другую технологию без изменения URI.
  • Используйте только строчные буквы.
  • Заменяйте пробелы дефисами или знаками подчеркивания (чем-то одним).
  • Старайтесь максимально избегать использования строк запросов.
  • Вместо использования кода 404 Not Found для URI, указывающих неполный путь, всегда предоставляйте в качестве ответа ресурс или страницу по умолчанию.

URI также должны быть статичными, чтобы при изменениях ресурсов или реализации сервиса ссылка оставалась той же. Это позволяет сохранить закладки. Также важно, чтобы взаимосвязи между ресурсами, закодированными в URI, оставались независимыми от способа указания местоположения ресурсов в хранилище.

Передача XML, JSON или обоих

Представление ресурса, как правило, отражает текущее состояние ресурса (и его атрибутов) на момент его запроса клиентским приложением. Представления ресурсов в этом смысле являются просто снимками состояния в конкретные моменты времени. Эти представления должны быть такими же простыми, как представление записи в базе данных, состоящее из отображения между именами столбцов и XML-тегами, где значения элементов в XML содержат значения строк. Если система имеет модель данных, то согласно этому определению представление ресурса является снимком состояния атрибутов одного из объектов модели данных системы. Это те объекты, которые будет обслуживать Web-сервис REST.

Последний набор ограничений, тесно связанный с дизайном Web-сервисов RESTful, относится к формату данных, которыми обмениваются приложение и сервис при работе в режиме запрос/ответ или в теле HTTP-запроса. Здесь особенно важны простота, читабельность и связанность.

Объекты модели данных обычно как-то связаны, и эти отношения между объектами (ресурсами) модели данных должны отражаться в способе их представления для передачи клиентскому приложению. В сервисе обсуждений пример представлений связанных ресурсов может включать в себя корневую тему обсуждения и ее атрибуты, а также встроенные ссылки на ответы, отправленные в эту тему.

XML-представление ветки обсуждения

Наконец, чтобы предоставить клиентским приложениям возможность запрашивать конкретный наиболее подходящий им тип содержимого, проектируйте сервис так, чтобы он использовал встроенный HTTP-заголовок Accept, значение которого является MIME-типом. Некоторые общеупотребительные MIME-типы, используемые RESTful-сервисами, перечислены в таблице 1.

Это позволит использовать сервис клиентским приложениям, написанным на разных языках и работающим на различных платформах и устройствах. Использование MIME-типов и HTTP-заголовка Accept представляет собой механизм согласования содержимого (content negotiation), позволяющий клиентским приложениям выбирать подходящий для них формат данных и минимизировать связность данных между сервисом и приложениями, его использующими.

Заключение

Технология REST – это способ проектирования Web-сервисов, менее зависимый от закрытого промежуточного программного обеспечения (например, сервер приложений), чем модели SOAP и WSDL. В некотором смысле REST, благодаря акценту на ранние интернет-стандарты URI и HTTP, является возвратом к Web до появления больших серверов приложений. Как вы увидели из рассмотрения принципов проектирования RESTful-интерфейса, XML поверх HTTP является мощным интерфейсом, позволяющим внутренним приложениям (например, пользовательским интерфейсам, основанным на технологии Ajax (Asynchronous JavaScript + XML)), легко подключаться и обращаться к ресурсам и потреблять их. Фактически именно хорошая совместимость Ajax и REST стала причиной сегодняшней популярности REST.

Представление системных ресурсов через RESTful API – это гибкий способ обеспечения различных приложений данными в стандартном формате. Это помогает выполнить требования к интеграции, имеющие решающее значение для создания систем, позволяющих легко комбинировать данные (машапов), а также для построения на основе набора базовых RESTful-сервисов более крупных конструкций.

Подходы к проектированию RESTful API

В этой статье я поделюсь опытом проектирования RESTful API — на конкретных примерах покажу, как делать хотя бы простые сервисы красиво. Также мы поговорим, что такое API и зачем он нужен, поговорим об основах REST — обсудим, на чем его можно реализовывать; коснемся основных веб-практик, которые зависят и не зависят от этой технологии. Также узнаем, как составлять хорошую документацию, затрачивая на это минимум усилий, и посмотрим, какие существуют способы нумерации версий для RESTful API.

Часть 1. Теория

Итак, как мы все знаем, API — application programming interface (интерфейс программирования приложений), набор правил и механизмов, с помощью которых одно приложение или компонент взаимодействует с другими

Почему хороший API — это важно?

  • Простота использования и поддержки. Хороший API просто использовать и поддерживать.
  • Хорошая конверсия в среде разработчиков. Если всем нравится ваш API, к вам приходят новые клиенты и пользователи.
  • Выше популярность вашего сервиса. Чем больше пользователей API, тем выше популярность вашего сервиса.
  • Лучше изоляция компонентов. Чем лучше структура API, тем лучше изоляция компонентов.
  • Хорошее впечатление о продукте. API — это как бы UI разработчиков; это то, на что разработчики обращают внимание в первую очередь при встрече с продуктом. Если API кривой, вы как технический эксперт не будете рекомендовать компаниям использовать такой продукт, приобретая что-то стороннее.

Теперь посмотрим, какие бывают виды API.

Виды API по способу реализации:

  • Web service APIs
    • XML-RPC and JSON-RPC
    • SOAP
    • REST
  • WebSockets APIs
  • Library-based APIs
    • Java Script
  • Class-based APIs
    • C# API
    • Java

Виды API по категориям применения:

  • OS function and routines
    • Access to file system
    • Access to user interface
  • Object remoting APIs
    • CORBA
    • .Net remoting
  • Hardware APIs
    • Video acceleration (OpenCL…)
    • Hard disk drives
    • PCI bus

Как мы видим, к Web API относятся XML-RPC и JSON-RPC, SOAP и REST.

RPC (remote procedure call — «удаленный вызов процедур») — понятие очень старое, объединяющие древние, средние и современные протоколы, которые позволяют вызвать метод в другом приложении. XML-RPC — протокол, появившийся в 1998 г. вскоре после появления XML. Изначально он поддерживался Microsoft, но вскоре Microsoft полностью переключилась на SOAP, поэтому в .Net Framework мы не найдем классов для поддержки этого протокола. Несмотря на это, XML-RPC продолжает жить до сих пор в различных языках (особенно в PHP) — видимо, заслужил любовь разработчиков простотой.

SOAP также появился в 1998 г. стараниями Microsoft. Он был анонсирован как революция в мире ПО. Нельзя сказать, что все пошло по плану Microsoft: было огромное количество критики из-за сложности и тяжеловесности протокола. В то же время, были и те, кто считал SOAP настоящим прорывом. Протокол продолжал развиваться и плодиться десятками новых и новых спецификаций, пока в 2003 г. W3C не утвердила в качестве рекомендации SOAP 1.2, который и сейчас — последний. Семейство у SOAP получилось внушительное: WS-Addressing, WS-Enumeration, WS-Eventing, WS-Transfer, WS-Trust, WS-Federation, Web Single Sign-On.

Затем, что закономерно, все же появился действительно простой подход — REST. Аббревиатура REST расшифровывается как representational state transfer — «передача состояния представления» или, лучше сказать, представление данных в удобном для клиента формате. Термин «REST» был введен Роем Филдингом в 2000 г. Основная идея REST в том, что каждое обращение к сервису переводит клиентское приложение в новое состояние. По сути, REST — не протокол и не стандарт, а подход, архитектурный стиль проектирования API.

Каковы принципы REST?

  • Клиент-серверная архитектура — без этого REST немыслим.
  • Любые данные — ресурс.
  • Любой ресурс имеет ID, по которому можно получить данные.
  • Ресурсы могут быть связаны между собой — для этого в составе ответа передается либо ID, либо, как чаще рекомендуется, ссылка. Но я пока не дошел до того, чтобы все было настолько хорошо, чтобы можно было легко использовать ссылки.
  • Используются стандартные методы HTTP (GET, POST, PUT, DELETE) — т. к. они уже заложены в составе протокола, мы их можем использовать для того, чтобы построить каркас взаимодействия с нашим сервером.
  • Сервер не хранит состояние — это значит, сервер не отделяет один вызов от другого, не сохраняет все сессии в памяти. Если у вас есть какое-либо масштабируемое облако, какая-то ферма из серверов, которая реализует ваш сервис, нет необходимости обеспечивать согласованность состояния этих сервисов между всеми узлами, которые у вас есть. Это сильно упрощает масштабирование — при добавлении еще одного узла все прекрасно работает.
  • Он очень прост!
  • Мы переиспользуем существующие стандарты, которые в ходу уже очень давно и применяются на многих устройствах.
  • REST основывается на HTTP => доступны все плюшки:
    • Кэширование.
    • Масштабирование.
    • Минимум накладных расходов.
    • Стандартные коды ошибок.
  • Очень хорошая распространенность (даже IoT-устройства уже умеют работать на HTTP).

Лучшие решения (независимые от технологий)

Какие в современном мире есть лучшие решения, не связанные с конкретной реализацией? Эти решения советую использовать обязательно:


  • SSL повсюду — самое важное в вашем сервисе, т. к. без SSL авторизация и аутентификация бессмысленны.
  • Документация и версионность сервиса — с первого дня работы.
  • Методы POST и PUT должны возвращать обратно объект, который они изменили или создали, — это позволит сократить время обращения к сервису вдвое.
  • Поддержка фильтрации, сортировки и постраничного вывода — очень желательно, чтобы это было стандартно и работало «из коробки».
  • Поддержка MediaType. MediaType — способ сказать серверу, в каком формате вы хотите получить содержимое. Если вы возьмете какую-либо стандартную реализацию web API и зайдете туда из браузера, API отдаст вам XML, а если зайдете через какой-нибудь Postman, он вернет JSON.
  • Prettyprint & gzip. Не минимизируйте запросы и не делайте компакт для JSON (того ответа, который придет от сервера). Накладные расходы на prettyprint —единицы процентов, что видно, если посмотреть, сколько занимают табы по отношению к общему размеру сообщения. Если вы уберете табы и будете присылать все в одну строку, запаритесь с отладкой. Что касается gzip, он дает выигрыш в разы. Т. ч. очень советую использовать и prettyprint, и gzip.
  • Используйте только стандартный механизм кэширования (ETag) и Last-Modified (дата последнего изменения) — этих двух параметров серверу достаточно, чтобы клиент понял, что содержимое не требует обновления. Придумывать что-то свое тут не имеет смысла.
  • Всегда используйте стандартные коды ошибок HTTP. Иначе вам однажды придется кому-нибудь объяснять, почему вы решили, что ошибку 419 в вашем проекте клиенту нужно трактовать именно так, как вы почему-то придумали. Это неудобно и некрасиво — за это клиент вам спасибо не скажет!

Свойства HTTP-методов

Сегодня мы будем говорить только про GET, POST, PUT, DELETE.

Если говорить вкратце об остальных, представленных в таблице, OPTIONS — получение настроек безопасности, HEAD — получение заголовков без тела сообщения, PATCH — частичное изменение содержимого.

Как вы видите, все методы, кроме POST, представленные в таблице, идемпотентны. Идемпотентность — возможность выполнить одно и то же обращение к сервису несколько раз, при этом ответ каждый раз будет одинаковым. Другими словами, не важно, по какой причине и сколько раз вы выполнили это действие. Допустим, вы выполняли действие по изменению объекта (PUT), и вам пришла ошибка. Вы не знаете, что ее вызвало и в какой момент, вы не знаете, изменился объект или нет. Но, благодаря идемпотентности, вы гарантированно можете выполнить этой действие еще раз, т. ч. клиенты могут быть спокойны за целостность своих данных.

«Safe» же значит, что обращение к серверу не изменяет содержимое. Так, GET может быть вызван много раз, но он не изменит никакого содержимого. Если бы он изменял содержимое, в силу того, что GET может быть закэширован, вам пришлось бы бороться с кэшированием, изобретать какие-нибудь хитрые параметры.

Часть 2. Практика

Выбираем технологию

Теперь, когда мы поняли, как работает REST, можем приступить к написанию RESTful API ¬ сервиса, отвечающего принципам REST. Начнем с выбора технологии.

Первый вариант — WCF Services. Все, кто работал с этой технологией, обычно возвращаться к ней больше не хотят — у нее есть серьезные недостатки и мало плюсов:
– webHttpBinding only (а зачем тогда остальные. ).
– Поддерживаются только HTTP Get & POST (и все).
+ Разные форматы XML, JSON, ATOM.

Второй вариант — Web API. В этом случае плюсы очевидны:
+ Очень простой.
+ Открытый исходный код.
+ Все возможности HTTP.
+ Все возможности MVC.
+ Легкий.
+ Тоже поддерживает кучу форматов.

Естественно, мы выбираем Web API. Теперь выберем подходящий хостинг для Web API.

Выбираем хостинг для Web API

Тут есть достаточно вариантов:

  • ASP.NET MVC (старый добрый).
  • Azure (облачная структура).
  • OWIN — Open Web Interface for .NET (свежая разработка от Microsoft).
  • IIS
  • Self-hosted

OWIN — не платформа и не библиотека, а спецификация, которая устраняет сильную связанность веб-приложения с реализацией сервера. Она позволяет запускать приложения на любой платформе, поддерживающей OWIN, без изменений. На самом деле, спецификация очень проста — это просто «словарь» из параметров и их значений. Базовые параметры определены в спецификации.

OWIN сводится к очень простой конструкции:

По схеме мы видим, что есть хост, на котором есть сервер, который поддерживает очень простой «словарь», состоящий из перечня «параметр — значение». Все модули, которые подключаются к этому серверу, конфигурируются именно так. Сервер, поддерживающий этот контракт, привязанный к определенной платформе, умеет распознавать все эти параметры и инициализировать инфраструктуру соответствующим образом. Получается, что, если вы пишете сервис, который работает с OWIN, можете свободно, без изменений кода, переносить его между платформами и использовать то же самое на других ОС.

Katana — реализация OWIN от Microsoft. Она позволяет размещать OWIN-сборки в IIS. Вот так она выглядит, очень просто:

Вы указываете, какой класс является у вас Startup. Это простой dll, который поднимается IIS. Вызывается конфигуратор. Этого кода достаточно, чтобы все заработало.

Проектируем интерфейс

Как пример возьмем простую модель с расписанием движения поездов на станциях. Вот примеры простейших запросов REST:

  • Корневые (независимые) сущности API:
    • GET /stations — получить все вокзалы.
    • GET /stations/123 — получить информацию по вокзалу с >Далее я еще расскажу про DDD, почему мы делаем именно так.

    Контроллер

    Итак, у нас есть станции, и теперь нам нужно написать простейший контроллер:

    Это роутинг, построенный на атрибутах. Здесь мы указываем имя контроллера и просим отдать список (в данном случае — случайные тестовые данные).

    OData (www.odata.org)

    Теперь представьте, что у вас больше данных, чем нужно на клиенте (больше ста тащить вряд ли имеет смысл). При этом писать самому какое-либо разбиение на страницы, конечно, совсем не хочется. Вместо этого есть простой способ — использовать легкую версию OData, которая поддерживается Web API.

    IQueryable позволяет вам использовать несколько простых, но эффективных механизмов фильтрации и управления данными на клиентской стороне. Единственное, что нужно сделать, — подключить OData-сборку из NuGet, указать EnableQuery и возвращать интерфейс iQueryable.

    Основное отличие такой облегченной верси от полноценной в том, что здесь нет контроллера, который возвращает метаданные. Полноценная OData немного изменяет ответ (заворачивает в спец. Обертку модель, которую вы собираетесь возвращать) и умеет возвращать связанное дерево объектов, которые вы хотите ей отдать. Также облегченная версия OData не умеет делать штуки вроде join, count и т. д.

    Параметры запросов

    А вот что можно делать:

    • $filter — фильтр, по имени, например. Все функции можно посмотреть на сайте OData — они очень помогают и позволяют существенно ограничить выборку.
    • $select — очень важная штука. Если у вас большая коллекция и все объекты толстые, но при этом вам нужно сформировать какой-то dropdown, в котором нет ничего, кроме ID и имени, которое вы хотите отобразить, — поможет эта функция, которая упростит и ускорит взаимодействие с сервером.
    • $orderby — сортировка.
    • $top и $skip — ограничение по выборкам.

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

    EnableQuery Attribute

    На самом деле OData — такая штука, которой очень легко можно выстрелить себе в ногу. Если у вас в таблице миллионы записей, и вам нужно тянуть их с сервера на клиент, это будет тяжело, а если таких запросов будет много — совсем смертельно.

    Именно для таких случаев у атрибута EnableQuery (см. код выше) есть такой набор параметров, с помощью которых очень многое можно ограничить: не давать больше строк, чем надо, не давать делать join, арифметические операции и т. д. При этом писать самому ничего не надо.

    • AllowedArithmeticOperators
    • AllowedFunctions
    • AllowedLogicalOperators
    • AllowedOrderByProperties
    • AllowedQueryOptions
    • EnableConstantParameterization
    • EnsureStableOrdering
    • HandleNullPropagation
    • MaxAnyAllExpressionDepth
    • MaxExpansionDepth
    • MaxNodeCount
    • MaxOrderByNodeCount
    • MaxSkip
    • MaxTop
    • PageSize

    Итак, вот примеры простейших запросов REST:

    • GET /stations – получить все вокзалы
    • GET /trains – расписание всех поездов
    • GET /stations/555/arrivals
    • GET /stations/555/departures

    Допустим, у нас есть вокзал 555, и мы хотим получить все его отправления и прибытия. Очевидно, что здесь должна использоваться сущность, которая зависит от сущности вокзала. Но как это сделать в контроллерах? Если мы все это будет делать роутинг-атрибутами и складывать в один класс, понятно, что в таком примере, как у нас, проблем нет. Но если у вас будет десяток вложенных сущностей и глубина будет расти еще дальше, все это вырастет в неподдерживаемый формат.

    И тут есть простое решение — в роутинг-атрибутах в контроллерах можно делать переменные:

    Соответственно, все зависимые сущности выносите в отдельный контроллер. Сколько их — совершенно неважно, так как они живут отдельно. С точки зрения Web API, они будут восприниматься разными контроллерами — сама система как бы не знает, что они зависимы, несмотря на то, что в URL они выглядят таковыми.

    Единственное, возникает проблема — здесь у нас «stations», и до этого был «stations». Если вы в одном месте что-то поменяете, а в другом — ничего не поменяете, ничего работать не будет. Однако тут есть простое решение — использование констант для роутинга:

    Тогда зависимый контроллер будет выглядеть так:

    Вы можете делать для зависимых контроллеров простейшие операции — вы просто берете и вычисляете роут сами, и тогда вы не ошибетесь. Кроме того, эти штуки полезно использовать в тестировании. Если вы хотите написать тест и потом хотите этим управлять, а не бегать каждый раз по всем миллионам ваших тестов и исправлять все строки, где указаны эти относительные URL’ы, то вы также можете использовать эти константы. Когда вы их меняете, данные у вас меняются везде. Это очень удобно.

    Итак, мы с вами обсудили, как могут выглядеть простейшие GET-операции. Все понимают, как сделать единичный GET. Но, кроме него, нам нужно обсудить еще три операции.

    • POST – создать новую сущность
      • POST /Stations – JSON-описание сущности целиком. Действие добавляет новую сущность в коллекцию.
      • Возвращает созданную сущность (во-первых, чтобы не было двойных походов к серверу, во-вторых, чтобы, если это нужно, вернуть со стороны сервера параметры, которые посчитались в этом объекте и нужны вам на клиенте).
    • PUT — изменить сущность
      • PUT /Stations/12 — Изменить сущность с >Еще примеры CRUD:

        • POST /Stations — добавляем вокзал.
        • POST /Stations/1/Departures — добавляем информацию об отправлении с вокзала 1.
        • DELETE /Stations/1/Departures/14 — удаляем запись об отправлении с вокзала 1.
        • GET /Stations/33/Departures/10/Tickets — список проданных билетов для отправления 10 с вокзала 33.

        Важно понимать, что узлы — обязательно какие-то сущности, то, что можно «потрогать» (билет, поезд, факт отправления поезда и т. д.).

        Антишаблоны

        А вот примеры, как делать не надо:

        • GET /Stations/?op=departure&train=11
          Здесь query string используется не только для передачи данных, но и для действий.
        • GET /Stations/DeleteAll
          Это реальный пример из жизни. Тут мы делаем GET на этот адрес, и он, по идее, должен удалить все сущности из коллекции — в итоге он ведет себя очень непредсказуемо из-за кэширования.
        • POST /GetUserActivity
          На самом деле здесь GET, который записан как POST. POST нужен был из-за параметров запроса в body, но в body у GET нельзя ничего передать — GET можно передать только в query string. GET даже по стандарту не поддерживает body.
        • POST /Stations/Create
          Здесь действие указано в составе URL — это избыточно.

        Проектируем API

        Допустим, у вас есть API, который вы хотите предложить людям, и есть доменная модель. Как связаны сущности API с доменной моделью? Да никак они не связаны, на самом деле. В этом нет никакой необходимости: то, что вы делаете в API, никак не связано с вашей внутренней доменной моделью.

        Может возникнуть вопрос, как проектировать API, если это не CRUD? Для этого мы записываем любые действия как команды на изменения. Мы делаем сохранение, чтение, удаление команды, GET, проверку статуса этой команды. GET из коллекции команд — вы получаете список всех команд, которые вы отправляли для какой-либо конкретной сущности.

        Доменная модель

        Мы поговорим о связи доменной модели с объектами. В примере у нас есть отель (Hotel), есть бронирования (Reservation), комнаты (Room) и устройства (Device), к ним привязанные. В нашем проекте это позволяло управлять комнатами посредством этих устройств.

        Но вот незадача: устройство — сущность, которая живет своей жизнью, и непонятно, как ее отделять от отеля. Но разделить отель и устройства, на самом деле, очень просто — поможет DDD. В первую очередь, нужно разобраться, где границы доменных областей и где границы сущностей, ответственных за согласованность системы.

        Bounded context (BC)

        Bounded context (изолированный поддомен) — фактически, наборы объектов, не зависимые друг от друга и имеющие совершенно независимые модели (разные). В примере мы можем взять и растащить отели и устройства на два разных BC — они не связаны между собой, но присутствует дублирование. Возникает дополнительная сущность (AttachedDevice):

        Тут у нас разные представления одного и того же устройства, и в этом нет ничего страшного.

        В DDD aggregate route — сущность, которая владеет всеми потомками. Это вершина нашего дерева (Hotel); то, за что можно вытянуть все остальное. А AttachedDevice так взять нельзя — его не существует, и он не имеет никакого смысла. Так же и классы Room и Reservation не имеют никакого смысла, будучи оторванными от Hotel. Поэтому доступ ко всем этим классам — исключительно через рутовую сущность, через Hotel, в данном случае. Device же — другой route с самого начала, другое дерево с другим набором полей.

        Итак, если вы понимаете, что одна сущность у вас играет в двух разных доменах, просто распилите ее — и это будет всего лишь проекция мастер-сущности. В AttachedDevice будут, например, поля с номером комнаты, а в Device такие поля не нужны.

        А вот примеры запросов, как они могут выглядеть в такой доменной модели:

        • PUT /hotels/555/rooms/105/attachedDevices — заменить всю коллекцию привязанных устройств на новую.
        • POST /hotels/555/rooms/105/attachedDevices — привязать еще одно устройство.
        • DELETE /hotels/12 — удалить описание отеля с >Я не буду сейчас рассказывать про это архитектуру, но хочу коротко обрисовать, в чем ее принцип действия. Архитектура CQRS основана на разделении потоков данных.

        У нас есть один поток, через который пользователь отправляет на сервер команду об изменении домена. Однако не факт, что изменение действительно произойдет, — пользователь не оперирует данными непосредственно. Итак, после того как пользователь посылает команду на изменение сущности, сервер ее обрабатывает и перекладывает в какую-то модель, которая оптимизирована на чтение — UI считывает это.

        Такой подход позволит вам следовать принципам REST очень легко. Если есть команда, значит, есть сущность «список команд».

        REST without PUT


        В простом CRUD-мире PUT — это штука, которая меняет объекты. Но если мы строго следуем принципу CQRS и делаем все через команды, PUT у нас пропадает, т. к. мы не можем менять объекты. Вместо этого можем лишь послать объекту команду на изменение. При этом можно отслеживать статус выполнения, отменять команды (DELETE), легко хранить историю изменений, а пользователь ничего не изменяет, а просто сообщает о намерениях.

        Парадигма REST without PUT — пока еще спорная и не до конца проверенная, но для каких-то случаев действительно хорошо применима.

        Fine-grained VS coarse-grained

        Представьте, что вы делаете большой сервис, большой объект. Тут у вас есть два подхода: fine-grained API и coarse-grained API («мелкозернистый» и «крупнозернистый» API).

        Fine-grained API:

        • Много маленьких объектов.
        • Бизнес-логика уходит на сторону клиента.
        • Нужно знать, как связаны объекты.
        • Создаете больше сущностей.
        • Сложно делать локальные изменения, например
          • POST /blogs//likes.
        • Нужно отслеживать состояние на клиенте.
        • Большие объекты нельзя сохранить частично.

        Для начала советую проектировать fine-grained API: каждый раз, когда вы создаете объект, отправляете его на сервер. На каждое действие на стороне клиента происходит обращение к серверу. Однако с маленькими сущностями работать проще, чем с большими: если вы напишете большую сущность, вам трудно будет потом ее распилить, трудно будет делать небольшие изменения и выдергивать из нее независимые куски. Т. ч. лучше начинать с маленьких сущностей и постепенно их укрупнять.

        Нумерация версий

        На прошлом проекте приходилось несколько раз откатывать API назад просто потому, что он был отдан клиенту — мы поменяли коды ошибок, но клиент уже успел привыкнуть к старым кодам.

        Какие известны на текущий момент варианты нумерации версий Web API?

        Самое простое — указать версию в URL.

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

        Вот один интересный готовый вариант.

        Это всего лишь роутинг атрибутов с constraint — если вы делали какие-либо серьезные объекты, наверняка делали constraint. По номеру версии в этом атрибуте ребята просто реализовали constraint. Соответственно, на один и тот же атрибут с разными версиями, но одинаковым именем контроллера вешаете на два разных класса и указываете разные версии. Все работает «из коробки….

        Документация

        Как видите, Swagger вытащил все, что у нас есть, вытащил XML-комментарии.

        Ниже — полное описание модели GET. Если нажать на кнопку, он ее в самом деле выполнит и вернет результат.

        А вот так выглядит документация к POST, первая часть:

        Вот вторая часть:

        Все, что было написано в XML-комментариях, — здесь.

        Руководство для начинающих по HTTP и REST

        Russian (Pусский) translation by Yuri Yuriev (you can also view the original English article)

        Протокол передачи гипертекста (HTTP) — это основа жизни в Интернете. Он используется каждый раз при передаче документа или в запросе AJAX . Но HTTP на удивление мало известен некоторым веб-разработчикам.

        В этом введении мы познакомимся с принципами разработки REST, лежащими в основе HTTP и использованию их мощи для создания интерфейсов, которые могут выполняться практически с любого устройства или операционной системы.

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

        Почему REST?

        REST — простой способ организации взаимодействия между независимыми системами.

        REST — простой способ организации взаимодействия между независимыми системами. Он пользуется популярностью с 2005 года и вдохновляет дизайн сервисов, таких как API Twitter. Благодаря тому, что REST обеспечивает взаимодействие с такими разнообразными клиентами, как мобильные телефоны и другие веб-сайты. Теоретически, REST не привязан к сети, но почти всегда реализован как таковой и был вдохновлен HTTP. В результате REST можно использовать везде, где возможен HTTP.

        Альтернативой является создание относительно сложных соглашений поверх HTTP. Часто это принимает форму новых XML-языков. Самый яркий пример — SOAP. Вам нужно выучить совершенно новый набор соглашений, но вы никогда не используете HTTP в полную силу. Поскольку REST был вдохновлён HTTP и играет на его сильные стороны, это лучший способ узнать, как работает HTTP.

        После первоначального обзора мы рассмотрим каждый из строительных блоков HTTP: URL-адреса, HTTP-команды и коды ответов. Мы также рассмотрим, как использовать их в RESTful. Попутно мы проиллюстрируем теорию примером приложения, которое имитирует процесс отслеживания данных, связанных с клиентами компании через веб-интерфейс.

        HTTP — это протокол, который позволяет отправлять документы в Интернете.

        HTTP — это протокол, который позволяет отправлять документы в Интернете. Протокол — это набор правил, определяющих, какие сообщения можно обменивать, и какие сообщения являются подходящими ответами для других. Другим распространенным протоколом является POP3, который вы можете использовать для извлечения электронной почты на вашем жёстком диске.

        В HTTP есть две разные роли: сервер и клиент. Как правило, клиент всегда инициирует разговор; сервер отвечает. HTTP основан на тексте; то есть сообщения по сути являются битами текста, хотя тело сообщения может также содержать другие носители. Использование текста позволяет легко отслеживать обмен HTTP.

        HTTP-сообщения состоят из заголовка и тела. Тело часто может оставаться пустым; оно содержит данные, которые вы хотите передать по сети, чтобы использовать их в соответствии с инструкциями в заголовке. Заголовок содержит метаданные, например информацию о кодировке; но, в случае запроса, он также содержит важные HTTP-методы. В стиле REST вы обнаружите, что данные заголовка часто более значимы, чем тела.

        Шпионство HTTP на работе

        Если вы используете Chrome Developer Tools или Firefox с расширением Firebug, щелкните на панели Net и установите его в enabled . После этого у вас будет возможность просматривать информацию о HTTP-запросах по мере вашего поиска. Например:

        Другим полезным способом ознакомиться с HTTP является использование выделенного клиента, такого как cURL.

        cURL — это инструмент command line, который доступен во всех основных операционных системах.

        После установки cURL введите:

        Будет отображен полный диалог HTTP. Запросам предшествует > , а ответам предшествует .

        URL — это, как вы определяете то, на чём вы хотите работать. Мы говорим, что каждый URL-адрес идентифицирует ресурс. Это те же самые URL-адреса, которые назначены веб-страницам. Фактически, веб-страница — это тип ресурса. Давайте рассмотрим более экзотический пример с нашим образцом приложения, которое управляет списком клиентов компании:

        будет идентифицировать всех клиентов, в то время как

        определяет клиента с именем ‘Jim’, предполагая, что он единственный с таким именем.

        В этих примерах мы обычно не включаем hostname в URL, так как это не имеет никакого отношения к организации интерфейса. Тем не менее, hostname важно для того, чтобы идентификатор ресурса был уникальным во всей сети. Мы часто говорим, что вы отправляете запрос на ресурс к host. Хост включается в заголовок отдельно от пути ресурса, который идёт прямо над заголовком запроса:

        Ресурсы лучше всего рассматривать как существительные. Например, следующее не RESTful:

        Это связано с тем, что для описания действия используется URL-адрес. Это довольно фундаментальный момент в различиях систем RESTful от систем без RESTful.

        Наконец, URL-адреса должны быть максимально точными; всё, что необходимо для уникальной идентификации ресурса, должно быть в URL-адресе. Вам не нужно включать в запрос данные, идентифицирующие ресурс. Таким образом, URL-адреса выступают в качестве полной карты всех данных, обрабатываемых приложением.

        Но как вы определяете действие? Например, как сообщить, что вы хотите создать новую запись клиента вместо её получения? Именно здесь вступают в действие глаголы HTTP.

        Глаголы HTTP

        Каждый запрос указывает определенный HTTP глагол или метод в заголовке запроса. Это первое слово в заголовке запроса. Например,

        означает, что используется метод GET, а

        означает использование метода DELETE .

        Глаголы HTTP сообщают серверу, что делать с данными, указанными URL.

        Глаголы HTTP сообщают серверу, что делать с данными, указанными URL. Запрос может ещё содержать дополнительную информацию в своем теле, которая может потребоваться для выполнения операции — например, данные, которые вы хотите сохранить с ресурсом. Эти данные можно указать в cURL с параметром -d .

        Если вы когда-либо создавали HTML-формы, то знакомы с двумя из наиболее важных HTTP-глаголов: GET и POST . Но есть гораздо больше доступных HTTP-глаголов. Наиболее важными для построения RESTful API являются GET , POST , PUT и DELETE . Доступны другие методы, такие как HEAD и OPTIONS , но они более редки (если вы хотите знать обо всех других методах HTTP, официальным источником является IETF).

        GET — это самый простой тип HTTP-запроса; которым браузер пользуется каждый раз, когда вы нажимаете ссылку или вводите URL-адрес в адресную строку. Он инструктирует сервер передавать клиенту данные, идентифицированные URL-адресом. Никогда не последует изменений данных на стороне сервера в результате запроса GET . В этом смысле GET -запрос доступен только для чтения, но, конечно, как только клиент получит данные, он может самостоятельно выполнять любые операции с ними, например, форматировать для отображения.

        Запрос PUT используется, когда вы хотите создать или обновить ресурс, указанный URL-адресом. Например,

        может создать клиента с именем Robin на сервере. Вы заметите, что REST полностью независим от бэкэнда; в запросе нет ничего, что информирует сервер о том, как данные должны создаваться — просто так должно быть. Это позволяет вам легко менять базовую технологию по необходимости. Запросы PUT содержат данные для использования при обновлении или создании ресурса в body. В cURL вы можете добавить данные в запрос с помощью -d .

        DELETE

        DELETE должен выполнять противоположное PUT ; его следует использовать, если вы хотите удалить ресурс, указанный URL-адресом запроса.

        Это приведёт к удалению всех данных, связанных с ресурсом, идентифицированных /clients/anne .

        POST используется, когда обработка, которую вы хотите выполнить на сервере, должна повторяться, если запрос POST повторяется (то есть, они не являются идемпотентными, подробнее об этом ниже). Кроме того, POST -запросы должны вызывать обработку body запроса как подчинённого URL-адреса, который вы отправляете.

        не должен вызывать изменение ресурса в /clients/ сам, но ресурс URL начинается с него /clients/ . Например, он может добавить в список нового клиента, с id , сгенерированным сервером.

        Запросы PUT легко используются вместо запросов POST и наоборот. Некоторые системы используют только один, некоторые используют POST для создания операций и PUT для операций обновления (поскольку с запросом PUT вы всегда указываете полный URL-адрес), некоторые используют POST для обновлений и PUT для создания.

        Часто запросы POST используются для запуска операций на сервере, которые не вписываются в парадигму Create/Update/Delete ; но это, однако, выходит за рамки REST . В нашем примере мы будем полностью придерживаться PUT .

        Классификация методов HTTP

        Помните: именно вы, программист, в конечном счете решаете, что происходит, когда используется определённый HTTP-метод. В реализациях HTTP нет ничего, что автоматически приведёт к созданию ресурсов, их перечислению, удалению или обновлению. Вы должны быть осторожны, чтобы правильно применять HTTP-протокол и вводить эту семантику самостоятельно.

        Представительства

        HTTP-клиент и HTTP-сервер обмениваются информацией о ресурсах, определённых URL-адресами.

        Мы можем суммировать то, что мы узнали до сих пор, следующим образом: HTTP-клиент и HTTP-сервер обмениваются информацией о ресурсах, определённых URL-адресами.

        Мы говорим, что запрос и ответ содержат представление ресурса. Под представлением мы подразумеваем информацию в определённом формате о состоянии ресурса или о том, каким это состояние должно быть в будущем. Оба, и header, и body — являются частями представления.


        Заголовки HTTP, содержащие метаданные, жёстко определяются спецификацией HTTP; они могут содержать только простой текст и должны быть отформатированы определённым образом.

        Тело может содержать данные в любом формате, и именно здесь видна сила HTTP. Вы знаете, что можете отправлять простой текст, изображения, HTML и XML на любом человеческом языке. Через метаданные запроса или различные URL-адреса вы можете выбирать между различными представлениями для одного и того же ресурса. Например, вы можете отправить веб-страницу в браузеры и JSON в приложения.

        HTTP-ответ должен указывать тип содержимого body. Это делается в заголовке, в поле Content-Type; например:

        Для простоты наше приложение только отправляет JSON туда и обратно, но приложение должно быть спроектировано таким образом, чтобы вы могли легко изменять формат данных, чтобы адаптироваться для разных клиентов или предпочтений пользователя.

        Библиотеки клиента HTTP

        cURL — это, чаще всего, HTTP-решение для PHP-разработчиков.

        Чтобы поэкспериментировать с различными методами запроса, вам нужен клиент, который позволит указать, какой метод использовать. К сожалению, формы HTML не подходят для счёта, так как они позволяют делать только запросы GET и POST. В реальной жизни API-интерфейсы доступны программно через отдельное клиентское приложение или через JavaScript в браузере.

        Именно поэтому в дополнение к серверу важно иметь хорошие возможности HTTP-клиента на выбранном вами языке программирования.

        Очень популярная клиентская HTTP-библиотека, опять же, cURL. Вы уже были ознакомлены с командой cURL ранее в этом уроке. CURL включает в себя как автономную программу командной строки, так и библиотеку, которая может использоваться различными языками программирования. В частности, cURL является, чаще всего, идеальным решением HTTP-клиента для разработчиков PHP. Другие языки, такие как Python, предлагают больше собственных клиентских HTTP-библиотек.

        Настройка примера приложения

        Я хочу показать как можно более низкий уровень функциональности.

        Наш пример PHP-приложения чрезвычайно тощ. Я хочу выставить как можно более низкоуровневую функциональность, без какой-либо магии framework . Я также не хотел использовать настоящий API, например, Twitter, потому что они могут неожиданно меняться, вам нужно настроить аутентификацию, что может быть проблемой и вы не сможете изучить реализацию.

        Чтобы запустить пример приложения, вам необходимо установить PHP5 и веб-сервер с механизмом для запуска PHP. Текущая версия должна быть не ниже версии 5.2, чтобы иметь доступ к функциям json_encode () и json_decode () .

        Что касается серверов, наиболее распространенным вариантом является Apache с mod_php, но вы можете использовать любые альтернативы, которые вам удобны. Существует пример конфигурации Apache, который содержит правила перезаписи, которые помогут вам быстро настроить приложение. Все запросы к любому URL, начиная с /clients/, должны быть направлены в наш файл server.php.

        В Apache вам нужно включить mod_rewrite и поместить прилагаемую конфигурацию mod_rewrite где-нибудь в вашей конфигурации Apache или в ваш файл .htacess. Таким образом, server.php будет отвечать на все запросы, поступающие с сервера. То же самое должно быть достигнуто с Nginx, или с любым другим сервером, который вы решите использовать.

        Как работает пример приложения

        Есть два ключа по обработке запросов REST. Первый ключ — инициировать различную обработку, в зависимости от метода HTTP, даже когда URL-адреса одинаковы. В PHP в глобальном массиве $ _SERVER есть переменная, которая определяет, какой метод был использован для выполнения запроса:

        Эта переменная содержит имя метода в виде строки, например ‘ GET ‘, ‘ PUT ‘ и далее.

        Другой ключ — узнать, какой URL был запрошен. Для этого мы используем другую стандартную переменную PHP:

        Эта переменная содержит URL-адрес, начинающийся с первой косой черты. Например, если имя хоста — ‘ example.com ‘, ‘ http://example.com/ ‘ вернётся ‘ / ‘, как ‘ http://example.com/test/ ‘ вернётся ‘ /test/ ‘.

        Давайте сначала попытаемся определить, какой URL-адрес был вызван. Мы рассматриваем только URL-адреса, начинающиеся с ‘ clients ‘. Все остальные недействительны.

        У нас есть два возможных результата:

        • Ресурс — это клиенты, и в этом случае мы возвращаем полный список
        • Существует еще один идентификатор

        Если есть ещё один идентификатор, мы предполагаем, что это имя клиента, и, опять же, перенаправляем его в другую функцию, в зависимости от method . Мы используем оператор switch , которого следует избегать в реальном приложении:

        Коды ответов

        Коды ответа HTTP стандартизируют способ информирования клиента о результате его запроса.

        Вы могли заметить, что пример приложения использует PHP header() , передавая некоторые странные строки в качестве аргументов. Функция header() печатает HTTP headers и гарантирует, что они отформатированы соответствующим образом. Заголовки должны быть первым в ответе, поэтому не стоит выводить что-либо ещё до того, как вы закончите с заголовками. Иногда ваш HTTP-сервер может быть настроен для добавления других заголовков в дополнение к тем, которые вы указали в коде.

        Заголовки содержат все виды метаинформации; например, текстовую кодировку, используемую в body сообщения, или MIME-тип содержимого body. В этом случае мы явно указываем коды ответа HTTP. Коды ответа HTTP стандартизируют способ информирования клиента о результате его запроса. По умолчанию PHP возвращает код ответа 200 , что означает, что ответ выполнен успешно.

        Сервер должен вернуть наиболее подходящий код ответа HTTP; таким образом клиент может попытаться исправить свои ошибки, если они есть. Большинство людей знакомы с распространенным кодом ответа 404 Not Found , однако есть много более доступных, в соответствии множеству ситуаций.

        Имейте в виду, что значение кода ответа HTTP не является чрезвычайно точным; это следствие того, что HTTP сам по себе довольно общий. Вы должны попытаться найти код ответа, который наиболее точно соответствует ситуации. Но и не слишком переживайте, если не сможете найти точное соответствие.

        Вот несколько HTTP-кодов ответа, которые часто используются с REST:

        200 OK

        Этот код ответа указывает, что запрос был успешным.

        201 Created

        Это означает, что запрос был успешным и был создан ресурс. Он используется в случае успеха запроса PUT или POST .

        400 Bad Request

        Запрос был утерян. Это происходит особенно с запросами POST и PUT , когда данные не проходят валидацию или находятся в неправильном формате.

        404 Not Found

        Этот ответ указывает, что необходимый ресурс не найден. Обычно это относится ко всем запросам, которые указывают на URL-адрес без соответствующего ресурса.

        401 Unauthorized

        Эта ошибка означает, что вам необходимо выполнить проверку подлинности перед доступом к ресурсу.

        405 Method Not Allowed

        Используемый метод HTTP не поддерживается для этого ресурса.

        409 Conflict

        Это указывает на конфликт. Например, вы используете запрос PUT для создания одного и того же ресурса дважды.

        500 Internal Server Error

        Когда всё остальное терпит неудачу; как правило, ответ 500 используется, когда обработка завершается неудачно из-за непредвиденных обстоятельств на стороне сервера, что вызывает ошибку сервера.

        Выполнение образца приложения

        Давайте начнем с простого извлечения информации из приложения. Нам нужны детали клиента, ‘ jim ‘, поэтому давайте отправим простой запрос GET на URL этого ресурса:

        Это отобразит полные сообщения headers. Последней строкой ответа будет body сообщения; в этом случае это будет JSON, содержащий адрес Jim (помните, что пропуск имени метода приведет к GET -запросу, а также замените localhost: 80 на имя сервера и порт, который вы используете).

        Затем мы можем получить информацию для всех клиентов одновременно:

        Чтобы создать нового клиента с именем Paul .

        и вы получите список всех клиентов, содержащих Paul в качестве подтверждения.

        Наконец, чтобы удалить клиента:

        Вы обнаружите, что возвращённый JSON больше не содержит никаких данных об Anne.

        Если вы пытаетесь получить несуществующего клиента, например:

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

        curl -v -X PUT http://localhost:80/clients/anne

        вместо этого получите ошибку 409.

        Заключение

        В общем, чем меньше предположений за пределами HTTP вы делаете, тем лучше.

        Важно помнить, что HTTP был задуман для взаимодействия между системами, которые ничто не разделяет, кроме понимания протокола. В целом, чем меньше допущений за пределами HTTP вы делаете, тем лучше: это позволяет широкому кругу программ и устройств получать доступ к вашему API.

        Я использовал PHP в этом уроке, потому что это, скорее всего, язык, наиболее знакомый читателям Nettuts +. Тем не менее, PHP, хотя и предназначен для Интернета, вероятно, не самый лучший язык для работы при REST-способе, поскольку он обрабатывает запросы PUT совсем иначе, чем GET и POST .

        Помимо PHP, вы можете принять во внимание следующее:

        • Различные Ruby frameworks (Rails и Sinatra)
        • В Python есть отличная поддержка REST. Должны работать Plain Django и WebOb, или Werkzeug.
        • node.js отлично поддерживает REST

        Среди приложений, которые пытаются придерживаться принципов REST, классическим примером является Atom Publishing Protocol, хотя на самом деле он не используется слишком часто на практике. За современным приложением, основанным на философии использования HTTP в полной мере, обратитесь к Apache CouchDB.

        REST API для взаимодействия сервисов

        Самым актуальный способом создать REST сервис в стеке технологий Майкрософт на сегодняшний день является ASP.NET Web API. До того эта технология значилась как WCF Web API и больше по названию тяготела к WCF. Но уже тогда там использовались сходные походы как в ASP.NET MVC, включая роутинг (routing). До нее существовали такие вещи как WCF 4 REST, WCF REST Starter Kit 3.5. Их все еще можно встретить на старых проектах и stackoverflow пестрит вопросами о них. Но то что ASP.NET Web API используется на новых проектах, а некоторые старые конвертируются, чтобы его использовать – радует. Так как предшественники были хуже как в плане технологии (приходилось писать много boilerplating code), удобства использования так и документации.

        В предыдущих постах были рассмотрены некоторые теоретические аспекты REST – теперь создадим простой REST сервис с помощью Web API и рассмотрим ключевые элементы такого сервиса.
        Начать стоит с подключения NuGet packages (и/или установки ASP.NET MVC):

        1. Web API, в случае если хостимся в ASP.NET:AspNetWebApi
        2. Self-hosted Web API:AspNetWebApi.Selfhost
        3. HttpClient включая XML и JSON форматеры:System.Net.Http.Formatting
        4. JsonValue для навигации и манипуляции JSON:System.Json

        В нашем случае, мы создадим просто сервис, который хостится на ASP.NET MVC, а также посмотрим на принцип создания интеграционных тестов к нему, которые будут поднимать self-hosted REST сервис в тестовом контексте. Акцент на Data access layer делятся не будет, если в процессе вам необходимо прикрутить DAL, например, с использованием Entity Framework Code First, то я писал об одном из возможных подходов раньше.

        Перед тем как создавать такой сервис необходимо также понимать что использовать Web API стоит если есть тесная связка с веб-клиентом, если сервис содержит логику выходящую за рамки CRUD операций. Если же у вас сервис по сути своей поставщик данных, т.е. операции в основном CRUD, то лучше использовать WCF Data Services, так как там много вещей из коробки генерится под базу — и CRUD операции и нормальная поддержка OData и IQuerable (в ASP.NET Web API она ограничена), которые позволяют делать запросы к сервису и данным с помощью Uri и специального OData синтаксиса.

        Итак преступим. Для начала создадим новый проект ASP.NET MVC4:

        Изображение 1
        Естественно темплейт (шаблон) для MVC 4 нагенерит нам типичную структуру ASP.NET MVC проекта (файл ValuesController я уже успел переименовать на DocumentsController). Отличие в дополнительном контроллере для Web API. По умолчанию это ValuesController, естественно его необходимо переименовать.

        В нашем случае он стал DocumentsController. Из темплейта этот контроллер содержит операции заглушки для Get, Post, Put, Delete. В просто случае переопределим эти операции для DocumentsController и ресурса Document. Получится вот такой вот контроллер:

        Это простой вариант, и здесь не используются фильтры для обработки сообщений или dependency resolvers. В свою очередь IDocumentRepository реализовано как простая заглушка и если дальше развивать тему с нормальным доступом к данным то реализацию можно подставить любую.
        Теперь проверим операции. Это сделать можно используя Fiddler и правильно сформировав запрос. Например операция получения всех документов, используем адрес http://127.0.0.1:81/api/documents/. Используется стандартный роутинг из коробки:

        Итак, запрос на http://127.0.0.1:81/api/documents/ должен вызвать метод IEnumerable Get() :

        Так и есть, нам вернулся список в виде XML из двух элементов. Теперь попробуем content negotiation из коробки в действии. К тому же самому вызову добавим HTTP заголовок – Accept:application/json. Итак запрос:

        Ответ ожидаем в Json:

        Из коробки идут два стандартных форматера – XML и Json, но есть возможность добавлять свои.

        Аналогичным образом будут работать остальные операции. Единственное попробуем еще запросить документ с недействительным идентификатором. Будем вызывать метод Document Get(string id) по адресу http://127.0.0.1:81/api/documents/9505a3b549b54881b3ed83fc19510534, где 9505a3b549b54881b3ed83fc19510534 – недействительный идентификатор, изменили последнюю цифру.

        Ожидается ответ 404 NotFound. Результат запроса:

        Вот таким вот образом можно создать и протестировать на работоспособность простенький REST сервис на базе ASP.NET Web API.

        Основные концепты — ApiController

        Так как мы имеем дело с REST сервисом. То из всего этого добра нас интересуют на начальном этапе контроллеры и роутинг. Контроллеры для Web API REST сервиса наследуются от от класса ApiController, который в свою очередь от интерфейса IHttpController. И ApiController несет с собой много добра, кроме конечно того что она автоматом распознается и выполняется. Из всего этого добра самое интересное являются свойства Request и Configuration.

        Основные концепты – Routing (Роутинг)

        При вызове операций с контроллера важный момент играет routing. Именно routing позволяет подсистеме WebApi связать Uri адрес и конкретную операцию из контроллера. Причем есть несколько вариантов — либо операция-action помечается атрибутом, либо используется договоренность именовать операции с префиксом – Http Verb. Например, в методе PostDocument – именно префикс Post позволяет сказать Web Api что эта операция связанна с Uri и вызывается по соответствующему адресу с HTTP Verb – POST.
        Еще одним вариантом для того, чтобы помочь выделить среди методов контроллера операции, которые связанны с URL – использование атрибутов — HttpGet, HttpPut, HttpPost, или HttpDelete, каждый из них соответствует такому же HTTP Verb – GET, PUT, POST, DELETE. Для того, чтобы навесить на операцию больше чем один HTTP Verb, или операцию отличную от 4 базовых (GET, PUT, POST, DELETE), используется атрибут – AcceptVerbs. Использование атрибутов также дает возможность отказаться от конвенции именования методов, когда префиксом выступает HTTP Verb.

        А для того чтобы избежать мапинга (mapping) метода как action используется атрибут NonAction без параметров.
        Есть еще способ роутинга, когда каждый мапинг делается по средством атрибутов на метод, а не глобальным роутингом через Global.asax.cs, но о нем позже, так как он не стандартный. Хотя на этапе WCF Web API использовался именно он.

        Routing по-умолчанию в Web API устанавливается как в методе RegisterRoutes на изображении 5 ниже. При использовании такого routing необходимо придерживаться конвенции именования методов в контроллере, когда каждый метод начинается с HTTP Verb префикса.

        Ну и естественно важная часть маппинга – routing в Global.asax.cs:

        Соответственно под роутинг «api//» подпадают URLs и примерные имена методов:
        Можно также сделать роутинг по имени action. Он не создается по-умолчанию темплейтом проекта. Например:
        В случае с таким роутингом необходимо использовать атрибуты HttpGet, HttpPut, HttpPost, HttpDelete или AcceptVerbs чтобы указать на какие методы мапить . В WCF WebAPI использовался роутинг с помощью атрибутов, его тоже можно прикрутить, но об этом отдельно.

        Основные концепты — HttpResponseMessage, HttpRequestMessage

        По сути это два спец класса которые используются достаточно часто. Они нужны для того чтобы иметь возможность оперировать запросом и ответом. HttpRequestMessage можно получить через свойство Request от ApiController (Изображение 6). Tак как Web API контроллеры всегда наследуются от ApiController, то его можно получить в середине любого из наших контроллеров. HttpRequestMessage позволяет нам управлять запросом, например извлекать из него данные из HTTP Body либо HTTP Headers которые нам нужны.

        HttpResponseMessage можно создать, чтобы вернуть результат, либо просто Response код (Изображение 7), либо еще и с нагрузкой, запаковав в его свойство Content, нужный нам HttpContent, например для бинарных данных подойдет наследник от HttpContent – StreamContent. Из свойства Request можно вычитать бинарные данные документа, который пришел с клиента:

        Возврат ошибок — HttpResponseException

        Вернуть ответ с ошибкой можно как с помощью HttpResponseMessage, указав код ошибки, так и с помощью специального класса HttpResponseException. Например, на изображении 7 на клиент возвращается ошибка InternalServerError = 500 с коротким описанием. Описание умеют читать далеко не все клиенты или клиентские библиотеки (были проблемы с iPad), в таком случае в тело сообщения с ошибкой можно писать объект более детально описывающий проблему, например свой кастомный объект с сообщением и кодом ошибки.

        Хостинг

        Само собой разумеется, что Web API REST сервис может хоститься на IIS либо вместе с ASP.NET MVC клиентом либо раздельно. Также его можно легко захостить вместе с ASP.NET MVC Web Role в облаке на Windows Azure. Но интересно, что Web API также можно хостить у себя в приложении, в памяти. Это значительно расширяет круг сценариев, в которых Web API может использоваться. Например с self-hosted Web API можно легко делать интеграционные тесты, которые поднимут во время тестирования self-hosted Web API сервис.

        Например, на изображение 8 ниже, показано как поднимается с self-hosted Web API сервис для интеграционного теста в методе BecauseOf.

        Клиент

        Клиентов к Web API REST может быть большое множество – есть куча библиотек под разные платформы для REST, можно обращаться к REST сервису c веб страницы по средством JavaScript и jQuery, можно использовать “старенький” класс WebClient для десктоп клиента. Вместе с Web API новым для .NET является также новый HttpClient, который очень легко использовать с десктоп клиента или тестового сценария (пример на изображении 8 метод should_make_tivial_get), и к тому же он изначально спроектирован асинхронным.

Добавить комментарий