Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос нашим менторам. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

REST

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

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

Существует множество технологий и протоколов, построенных на идеях RPC, например SOAP, json-rpc, xml-rpc, CORBA, D-Bus и другие. Многие из них достаточно сложны в использовании и реализации.

С появлением интернета стали набирать обороты веб-сервисы, сайты, предоставляющие API для взаимодействия с ними. Попытки стандартизировать это взаимодействие привели к тому, что идеи RPC стали применяться и в вебе.

Попробую на примерах раскрыть суть этого подхода с использованием http. Он состоит в том, что определяется единая точка входа в приложение, например, /myapi через которую, как ожидается, будут передаваться команды для управления сервисом. Можем предположить, что в простейшем случае действие определяется ассоциативным массивом из трёх значений type, action, data. Посмотрим, как в таком случае будет выглядеть работа с сервисом (обратите внимание, что ниже описаны запросы к сервису со стороны клиента).

// Создать пользователя
const response1 = await axios.post('/myapi', {
  type: 'user',
  action: 'create',
  data: {
    nickname: 'malloc',
  },
});

// Выполнить рассылку писем
const response2 = await axios.post('/myapi', {
  type: 'maillist',
  action: 'send',
  data: {
    id: 244,
  },
});

// Удалить проект
const response3 = await axios.post('/myapi', {
  type: 'project',
  action: 'delete',
  data: {
    id: 11,
  },
});

Как видно из примеров выше, http участвует в процессе только как способ передать данные на сервер. Это и называется транспортный протокол (хотя с точки зрения OSI, http не является транспортным протоколом, но это не важно, реальная жизнь не всегда совпадает с теоретическими моделями). А сама структура сообщений должна напоминать вам вызовы обычных функций, только расположенных на другом сервисе. В этом и есть вся суть удалённого вызова процедур.

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

Запрос:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
     <getProductDetails xmlns="http://warehouse.example.com/ws">
       <productID>12345</productID>
     </getProductDetails>
   </soap:Body>
</soap:Envelope>

Ответ:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
     <getProductDetailsResponse xmlns="http://warehouse.example.com/ws">
       <getProductDetailsResult>
         <productID>12345</productID>
         <productName>Стакан граненый</productName>
         <description>Стакан граненый. 250 мл.</description>
         <price>9.95</price>
         <currency>
             <code>840</code>
             <alpha3>USD</alpha3>
             <sign>$</sign>
             <name>US dollar</name>
             <accuracy>2</accuracy>
         </currency>
         <inStock>true</inStock>
       </getProductDetailsResult>
     </getProductDetailsResponse>
   </soap:Body>
</soap:Envelope>

Принцип точно такой же, как и в примере выше. Структура тела запроса целиком описывает то, что нужно делать, и http к этому никак не причастен, его задача доставить сообщение и вернуть ответ.

Ирония судьбы в том, что изначально SOAP расшифровывался как Simple Object Access Protocol, что являлось прямой противоположностью тому, чем он являлся. Сложность SOAP, многословность, медленная работа — всё это привело к тому, что использовать его стали сильно меньше, и современные web приложения часто выбирают REST-like подход.

Перед тем, как перейти к REST, нельзя не упомянуть о хаотичном RPC подходе. Это подход (имя которому я сейчас придумал) уже частично использует http как прикладной протокол, но всё так же ориентирован на вызов удалённых процедур, то есть на действия.

Запросы в этом стиле выполняются так:

// адреса в этих запросах не идентифицируют ресурсы, они говорят только о действии
axios.post('/removeUser', { id: 5 });
axios.get('/getCountriesCount');
axios.post('/setProjectName', { name: 'new name' });

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

Representational State Transfer (REST)

Термин "REST" был введён Роем Филдингом, одним из создателей протокола "HTTP", лишь в 2000 году. В своей диссертации "Архитектурные стили и дизайн сетевых программных архитектур" ("Architectural Styles and the Design of Network-based Software Architectures") в Калифорнийском университете в Ирвайне он подвёл теоретическую основу под способ взаимодействия клиентов и серверов во Всемирной паутине, абстрагировав его и назвав "передачей представительного состояния".

В отличие от веб-сервисов на основе SOAP, не существует "официального" стандарта для RESTful веб-API. Дело в том, что REST является архитектурным стилем (я бы даже сказал набором практик), в то время как SOAP является протоколом.

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

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

  1. Идентификация. В отличие от RPC, в котором протоколы ориентированы на действия, в REST стиле подразумевается ориентация на ресурсы (существительное). Взаимодействие с каждым ресурсом происходит посредством представлений ресурса, запрашиваемых по URN, который идентифицирует конкретный ресурс. То есть, мы никогда не взаимодействуем с самим ресурсом напрямую, а получаем лишь его представления, которых может быть много. Сервер может отдать данные в формате json или html, хотя при этом ни один из них не является реальным типом хранения внутри сервера.

  2. Манипуляция ресурсами через представление. Если клиент хранит представление ресурса, включая метаданные - он имеет достаточно данных для модификации или удаления ресурса.

  3. "Самоописываемые" сообщения. Каждое сообщение содержит достаточно информации, чтобы описать, каким образом его обрабатывать. К примеру, какой парсер необходимо применить для извлечения данных, может быть описано в Internet медиа-типе, другими словами, посылая на сервер json в теле http запроса, REST диктует обязательную установку типа контента в заголовке. В общем случае для обработки сообщения должно быть достаточно информации из самого сообщения (всё, что может передаваться по http).

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

| GET       | /photos          | display a list of all photos                 |
| GET       | /photos/new      | return an HTML form for creating a new photo |
| POST      | /photos          | create a new photo                           |
| GET       | /photos/:id      | display a specific photo                     |
| GET       | /photos/:id/edit | return an HTML form for editing a photo      |
| PATCH/PUT | /photos/:id      | update a specific photo                      |
| DELETE    | /photos/:id      | delete a specific photo                      |

Кэширование

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

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

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

Примером такого состояния является корзина в интернет магазине. Если она привязана к пользовательской сессии и хранится на конкретном сервере (а не в браузере клиента), то отправить запрос пользователя на другой сервер станет невозможно. Он просто не увидит своей корзины. Эту проблему можно решить двумя способами. Первый - хранить её на клиенте, используя, например, cookie. Вторая - изменением способа хранения корзины на сервере и помещением её в базу данных, которая доступна со всех серверов.

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

Клиент-серверная архитектура

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

Слои

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

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

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

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

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

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

По-простому

Грубо говоря, REST это набор рекомендаций о том, как лучше сделать для получения преимуществ, описанных абзацем выше. Чем больше рекомендаций вы выполните, тем более REST получается приложение. А поскольку жизнь сложна, то это нормально, что REST ориентированными сервисы являются только отчасти. Использования REST как догмы ни к чему хорошему не приведёт. И REST никоим образом не заменяет собой RPC. Выбор решения зависит от ситуации и предъявляемых требований.

JSONAPI

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

Есть и другой путь. В 2013 году появился стандарт под названием jsonapi, в котором очень подробно описано как создавать REST-like сервисы. Для его реализации написано множество библиотек под все популярные языки программирования. Как минимум, я рекомендую с ним ознакомиться, а ещё лучше - взять на вооружение. Использование открытых стандартов в промышленном программировании делает нашу жизнь проще, бизнес богаче, а волосы шелковистее.


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят менторы из команды Хекслета или другие студенты.

Для полного доступа к курсу нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Нажимая кнопку «Зарегистрироваться», вы даёте своё согласие на обработку персональных данных в соответствии с «Политикой конфиденциальности» и соглашаетесь с «Условиями оказания услуг». Защита от спама reCAPTCHA «Конфиденциальность» и «Условия использования».