mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-03-03 15:22:13 +02:00
deconstructing the REST, part II
This commit is contained in:
parent
2893748266
commit
b1d99a8d1c
@ -1,12 +1,12 @@
|
||||
### Мифология REST
|
||||
|
||||
#### История и фактология
|
||||
#### Матчасть
|
||||
|
||||
Мало какая технология в истории IT вызывала столько ожесточённых споров, как REST. Самое удивительное при этом состоит в том, что спорящие стороны, как правило, совершенно не представляют себе предмет спора.
|
||||
|
||||
Начнём с самого начала. В 2000 году один из авторов спецификаций HTTP и URI Рой Фьелдинг защитил докторскую диссертацию на тему «Архитектурные стили и дизайн архитектуры сетевого программного обеспечения», пятая глава которой была озаглавлена как «Representational State Transfer (REST)». Вот она: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm.
|
||||
Начнём с самого начала. В 2000 году один из авторов спецификаций HTTP и URI Рой Филдинг защитил докторскую диссертацию на тему «Архитектурные стили и дизайн архитектуры сетевого программного обеспечения», пятая глава которой была озаглавлена как «Representational State Transfer (REST)». Диссертация доступна [по ссылке](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm).
|
||||
|
||||
Как нетрудно убедиться, прочитав эту главу, она представляет собой довольно абстрактный обзор распределённой сетевой архитектуры, вообще не привязанный ни к HTTP, ни к URL. Более того, глава вообще-то посвящена вовсе не правилам дизайна API; в ней Фьелдинг методично перечисляет ограничения, с которыми приходится сталкиваться разработчику распределённого сетевого программного обеспечения. Вот они:
|
||||
Как нетрудно убедиться, прочитав эту главу, она представляет собой довольно абстрактный обзор распределённой сетевой архитектуры, вообще не привязанный ни к HTTP, ни к URL. Более того, она вовсе не посвящена правилам дизайна API; в этой главе Филдинг методично перечисляет ограничения, с которыми приходится сталкиваться разработчику распределённого сетевого программного обеспечения. Вот они:
|
||||
|
||||
* клиент и сервер не знают внутреннего устройства друг друга (клиент-серверная архитектура);
|
||||
* сессия хранится на клиенте (stateless-дизайн);
|
||||
@ -15,36 +15,41 @@
|
||||
* сетевые системы являются многослойными, т.е. сервер может быть только прокси к другим серверам;
|
||||
* функциональность клиента может быть расширена через поставку кода с сервера.
|
||||
|
||||
Всё, на этом определение REST заканчивается. Дальше Фьелдинг конкретизирует некоторые аспекты имплементации систем в указанных ограничениях, но все они точно так же являются совершенно абстрактными. Буквально: «ключевая информационная абстракция в REST — ресурс; любая информация, которую можно назвать, может быть ресурсом».
|
||||
Всё, на этом определение REST заканчивается. Дальше Филдинг конкретизирует некоторые аспекты имплементации систем в указанных ограничениях, но все они точно так же являются совершенно абстрактными. Буквально: «ключевая информационная абстракция в REST — ресурс; любая информация, которой можно дать наименование, может быть ресурсом».
|
||||
|
||||
Ключевой вывод, который следует из определения REST по Фьелдингу, вообще-то, таков: *любое сетевое ПО в мире соответствует принципам REST*, за очень-очень редкими исключениями.
|
||||
Ключевой вывод, который следует из определения REST по Филдингу, вообще-то, таков: *любое сетевое ПО в мире соответствует принципам REST*, за очень-очень редкими исключениями.
|
||||
|
||||
В самом деле, я с большим трудом могу себе представить систему, в которой не было хоть какого-нибудь стандартизованного интерфейса взаимодействия, иначе её просто невозможно будет разрабатывать. Раз есть интерфейс взаимодействия, значит, под него всегда можно мимикрировать, а значит, требование независимости имплементации клиента и сервера всегда выполнено. Раз можно сделать альтернативную имплементацию сервера — значит, можно сделать и многослойную архитектуру, поставив дополнительный прокси между клиентом и сервером. Поскольку клиент представляет собой вычислительную машину - он всегда хранит хоть какое-то состояние и кэширует хоть какие-то данные. Наконец, code-on-demand вообще лукавое требование, поскольку всегда можно объявить данные, полученные по сети, «инструкциями» на некотором формальном языке, а код клиента — их интерпретатором.
|
||||
В самом деле:
|
||||
* очень сложно представить себе систему, в которой не было бы хоть какого-нибудь стандартизованного интерфейса взаимодействия, иначе её просто невозможно будет разрабатывать;
|
||||
* раз есть интерфейс взаимодействия, значит, под него всегда можно мимикрировать, а значит, требование независимости имплементации клиента и сервера всегда выполнимо;
|
||||
* раз можно сделать альтернативную имплементацию сервера — значит, можно сделать и многослойную архитектуру, поставив дополнительный прокси между клиентом и сервером;
|
||||
* поскольку клиент представляет собой вычислительную машину, он всегда хранит хоть какое-то состояние и кэширует хоть какие-то данные;
|
||||
* наконец, code-on-demand вообще лукавое требование, поскольку всегда можно объявить данные, полученные по сети, «инструкциями» на некотором формальном языке, а код клиента — их интерпретатором.
|
||||
|
||||
Да, конечно, вышеприведённое рассуждение является софизмом, доведением до абсурда. Самое забавное в этом упражнении состоит в том, что мы можем довести его до абсурда и в другую сторону, объявив ограничения REST неисполнимыми. Например, очевидно, что требование code-on-demand противоречит требованию независимости клиента и сервера — клиент должен уметь интерпретировать код с сервера, написанный на вполне конкретном языке. Что касается правила на букву S («stateless»), то систем, в которых сервер *вообще не хранит никакого контекста клиента* в мире вообще практически нет, поскольку ничего полезного для клиента в такой системе сделать нельзя. (Что, кстати, постулируется в соответствующем разделе прямым текстом: «клиент не может получать никаких преимуществ от того, что на сервере хранится какой-то контекст».)
|
||||
|
||||
Наконец, сам Фьелдинг внёс дополнительную энтропию в вопрос, выпустив в 2008 году [разъяснение](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven), что же он имел в виду. В частности, в этой статье утверждается, что:
|
||||
Наконец, сам Филдинг внёс дополнительную энтропию в вопрос, выпустив в 2008 году [разъяснение](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven), что же он имел в виду. В частности, в этой статье утверждается, что:
|
||||
* REST API не должно зависеть от протокола;
|
||||
* разработка REST API должна фокусироваться на описании медиатипов, представляющих ресурсы; при этом клиент вообще ничего про эти медиатипы знать не должен;
|
||||
* в REST API не должно быть фиксированных имён ресурсов и операций над ними, клиент должен извлекать эту информацию из ответов сервера.
|
||||
|
||||
Короче говоря, REST по Фьелдингу подразумевает, что клиент, получив каким-то образом ссылку на точку входа REST API, далее должен быть в состоянии полностью выстроить взаимодействие с этим API, не обладая вообще никаким априорным знанием о нём, и уж тем более не должен содержать никакого специально написанного кода, чтобы с этим API взаимодействовать.
|
||||
Короче говоря, REST по Филдингу подразумевает, что клиент, получив каким-то образом ссылку на точку входа REST API, далее должен быть в состоянии полностью выстроить взаимодействие с этим API, не обладая вообще никаким априорным знанием о нём, и уж тем более не должен содержать никакого специально написанного кода, чтобы с этим API взаимодействовать.
|
||||
|
||||
Оставляя за скобками тот факт, что Фьелдинг весьма вольно истолковал свою же диссертацию, просто отметим, что ни одна существующая система в мире не удовлетворяет описанию REST по Фьелдингу-2008.
|
||||
Оставляя за скобками тот факт, что Филдинг весьма вольно истолковал свою же диссертацию, просто отметим, что ни одна существующая система в мире не удовлетворяет описанию REST по Филдингу-2008.
|
||||
|
||||
#### Здравое зерно REST
|
||||
|
||||
Нам неизвестно, почему из всех обзоров абстрактной сетевой архитектуры именно диссертация Фьелдинга обрела столь широкую популярность; очевидно другое: теория Фьелдинга, преломившись в умах миллионов программистов (включая самого Фьелдинга), превратилась в целую инженерную субкультуру. Путём редукции абстракций REST применительно конкретно к протоколу HTTP и стандарту URL родилась химера «RESTful API», [конкретного смысла которой никто не знает](https://restfulapi.net/).
|
||||
Нам неизвестно, почему из всех обзоров абстрактной сетевой архитектуры именно диссертация Филдинга обрела столь широкую популярность; очевидно другое: теория Филдинга, преломившись в умах миллионов программистов (включая самого Филдинга), превратилась в целую инженерную субкультуру. Путём редукции абстракций REST применительно конкретно к протоколу HTTP и стандарту URL родилась химера «RESTful API», [конкретного смысла которой никто не знает](https://restfulapi.net/).
|
||||
|
||||
Хотим ли мы тем самым сказать, что REST является бессмысленной концепцией? Отнюдь нет. Мы только хотели показать, что она допускает чересчур широкую интерпретацию, в чём одновременно кроется и её сила, и её слабость.
|
||||
|
||||
С одной стороны, благодаря многообразию интерпретаций, разработчики API выстроили какое-то размытое, но всё-таки полезное представление о «правильной» архитектуре API. С другой стороны, если бы Фьелдинг чётко расписал в 2000 году, что же он конкретно имел в виду, вряд ли бы об этой диссертации знало больше пары десятков человек.
|
||||
С одной стороны, благодаря многообразию интерпретаций, разработчики API выстроили какое-то размытое, но всё-таки полезное представление о «правильной» архитектуре API. С другой стороны, если бы Филдинг чётко расписал в 2000 году, что же он конкретно имел в виду, вряд ли бы об этой диссертации знало больше пары десятков человек.
|
||||
|
||||
Что же «правильного» в REST-подходе к дизайну API (таком, как он сформировался в коллективном сознании широких масс программистов)? То, что такой дизайн позволяет добиться двух целей: более эффективно использовать время программистов и машинное время.
|
||||
Что же «правильного» в REST-подходе к дизайну API (таком, как он сформировался в коллективном сознании широких масс программистов)? То, что такой дизайн позволяет добиться более эффективного использования времени — времени программистов и компьютеров.
|
||||
|
||||
Если коротко обобщить все сломанные в попытках определить REST копья, то мы получим следующий принцип: *хорошо*, если агенты распределённой системы могут читать мета-информацию запросов и ответов, проходящих через систему.
|
||||
Если коротко обобщить все сломанные в попытках определить REST копья, то мы получим следующий принцип: *лучше бы агентам распределённой системы понимать мета-информацию запросов и ответов, проходящих через эту систему* (готовая заповедь для RESTful-пастафарианства практически!).
|
||||
|
||||
У протокола HTTP есть очень важное достоинство: он предоставляет стороннему наблюдателю довольно подробную информацию о том, что произошло с запросом и ответом, даже если этот наблюдатель ничего не знает о их семантике:
|
||||
У протокола HTTP есть очень важное достоинство: он предоставляет стороннему наблюдателю довольно подробную информацию о том, что произошло с запросом и ответом, даже если этот наблюдатель ничего не знает о семантике операции:
|
||||
|
||||
* URL идентифицирует некоего конечного получателя запроса, какую-то единицу адресации;
|
||||
* по статусу ответа можно понять, выполнена ли операция успешно или произошла ошибка; если имеет место быть ошибка — то можно понять, кто виноват (клиент или сервер), и даже в каких-то случаях понять, что же конкретно произошло;
|
||||
@ -53,11 +58,11 @@
|
||||
|
||||
Немаловажно здесь то, что все эти сведения можно получить, не вычитывая тело ответа целиком — достаточно прочитать служебную информацию из заголовков.
|
||||
|
||||
Почему это *полезно*? Потому что современный стек взаимодействия между клиентом и сервером является довольно сложным. Разработчик пишет код поверх какого-то фреймворка, который отправляет запросы; фреймворк базируется на API языка программирования, которое, в свою очередь, обращается к API операционной системы. Далее запрос (возможно, через промежуточные HTTP-прокси) доходит до сервера, который, в свою очередь, тоже представляет собой несколько слоёв абстракции в виде фреймворка, языка программирования и ОС; к тому же, перед конечным сервером, как правило, находится веб-сервер, проксирующий запрос, а зачастую и не один. В современных облачных архитектурах HTTP-запрос, прежде чем дойти до конечного обработчика, пройдёт через несколько абстракций в виде прокси и gateway-ев. Если бы все эти агенты трактовали мета-информацию о запросе одинаково, это позволило бы трактовать многие ситуации оптимальнее.
|
||||
Почему это *полезно*? Потому что современный стек взаимодействия между клиентом и сервером является (как предсказывал Филдинг) многослойным. Разработчик пишет код поверх какого-то фреймворка, который отправляет запросы; фреймворк базируется на API языка программирования, которое, в свою очередь, обращается к API операционной системы. Далее запрос (возможно, через промежуточные HTTP-прокси) доходит до сервера, который, в свою очередь, тоже представляет собой несколько слоёв абстракции в виде фреймворка, языка программирования и ОС; к тому же, перед конечным сервером, как правило, находится веб-сервер, проксирующий запрос, а зачастую и не один. В современных облачных архитектурах HTTP-запрос, прежде чем дойти до конечного обработчика, пройдёт через несколько абстракций в виде прокси и гейтвеев. Если бы все эти агенты трактовали мета-информацию о запросе одинаково, это позволило бы обрабатывать многие ситуации оптимальнее — тратить меньше ресурсов и писать меньше кода.
|
||||
|
||||
(На самом деле, в отношении многих технических аспектов промежуточные агенты и так позволяют себе разные вольности, не спрашивая разработчиков. Например, свободно менять Accept-Encoding и Content-Length.)
|
||||
(На самом деле, в отношении многих технических аспектов промежуточные агенты и так позволяют себе разные вольности, не спрашивая разработчиков. Например, свободно менять Accept-Encoding и Content-Length при проксировании запросов.)
|
||||
|
||||
Каждый из аспектов, перечисленных Фьелдингом в REST-принципах, позволяет лучше организовать работу промежуточного ПО. Ключевым здесь является stateless-принцип: промежуточные прокси могут быть уверены, что метаинформация запроса его однозначно описывает.
|
||||
Каждый из аспектов, перечисленных Филдингом в REST-принципах, позволяет лучше организовать работу промежуточного ПО. Ключевым здесь является stateless-принцип: промежуточные прокси могут быть уверены, что метаинформация запроса его однозначно описывает.
|
||||
|
||||
Приведём простой пример. Пусть в нашей системе есть операции получения профиля пользователя и его удаления. Мы можем организовать их разными способами. Например, вот так:
|
||||
|
||||
@ -102,12 +107,61 @@ Authorization: Bearer <token>
|
||||
|
||||
Теперь URL запроса в точности идентифицирует ресурс, к которому обращаются; теперь можно, в частности, организовать маршрутизацию запроса в зависимости от идентификатора пользователя. Префетчер мессенджера не пройдёт по DELETE-ссылке; а если он это и сделает, то без заголовка Authorization операция выполнена не будет.
|
||||
|
||||
Наконец, неочевидная польза такого решения заключается в следующем: промежуточный сервер-гейтвей, обрабатывающий запрос, может проверить заголовок Authorization и переслать запрос далее без него (желательно, конечно, по безопасному соединению или хотя бы подписав запрос). Тогда во внутренней среде можно будет свободно организовывать кэширование данных в любых промежуточных узлах. Более того, гейтвей может модифицировать операцию: например, для авторизованных пользователей пересылать запрос дальше как есть, а для неавторизованным показывать публичный профиль, пересылая запрос на специальный URL, ну, скажем, `GET /user/{user_id}/public-profile`.
|
||||
Наконец, неочевидная польза такого решения заключается в следующем: промежуточный сервер-гейтвей, обрабатывающий запрос, может проверить заголовок Authorization и переслать запрос далее без него (желательно, конечно, по безопасному соединению или хотя бы подписав запрос). Тогда во внутренней среде можно будет свободно организовывать кэширование данных в любых промежуточных узлах. Более того, *агент может легко модифицировать операцию*: например, для авторизованных пользователей пересылать запрос дальше как есть, а для неавторизованным показывать публичный профиль, пересылая запрос на специальный URL, ну, скажем, `GET /user/{user_id}/public-profile`. Для современных микросервисных архитектур *возможность корректно и дёшево модифицировать запрос при маршрутизации и является самым ценным преимуществом в концепции REST*.
|
||||
|
||||
**Разумеется** все подобные оптимизации можно выполнить и без опоры на стандартную номенклатуру методов / статусов / заголовков HTTP, или даже вовсе поверх другого протокола. Достаточно разработать одинаковый формат данных, содержащий нужную мета-информацию, и научить промежуточные агенты его читать. В общем-то, именно это Фьелдинг и утверждает в своей диссертации.
|
||||
Шагнём ещё чуть вперёд. Предположим, что гейтвей спроксировал запрос `DELETE /user/{user_id}` в нужный микросервис и не дождался ответа. Какие дальше возможны варианты?
|
||||
|
||||
**Вариант 1**. Можно сгенерировать HTML-страницу с ошибкой, вернуть её веб-серверу, чтобы тот вернул её клиенту, чтобы клиент показал её пользователю, и дождаться реакции пользователя. Мы прогнали через систему сколько-то байтов и переложили решение проблемы на конечного потребителя. Попутно заметим, что при этом на уровне логов веб-сервера ошибка неотличима от успеха — и там, и там какой-то немашиночитаемый ответ со статусом 200 — и, если действительно пропала сетевая связность между гейтвеем и микросервисом, об этом никто не узнает.
|
||||
|
||||
**Вариант 2**. Можно вернуть веб-серверу подходящую HTTP-ошибку, например, 504, чтобы тот вернул её клиенту, чтобы клиент обработал ошибку и, сообразно своей логике, что-то предпринял по этому поводу, например, отправил запрос повторно или показал ошибку пользователю. Мы прокачали чуть меньше байтов, попутно залогировав исключительную ситуацию, но теперь переложили решение на разработчика клиента — это ему надлежит не забыть написать код, который умеет работать с ошибкой 504.
|
||||
|
||||
**Вариант 3**. Гейтвей, зная, что метод `DELETE` идемпотентен, может сам повторить запрос; если исполнить запрос не получилось — проследовать по варианту 1 или 2. В этой ситуации мы переложили ответственность за решение на архитектора системы, который должен спроектировать политику перезапросов внутри неё (и гарантировать, что все операции за `DELETE` действительно идемпотенты), но мы получили важное свойство: система стала самовосстанавливающейся. Теперь она может «сама» побороть какие-то ситуации, которые раньше вызывали исключения.
|
||||
|
||||
Внимательный читатель может заметить, что вариант (3) при этом является наиболее технически сложным из всех, поскольку включает в себя варианты (1) и (2): для правильной работы всей схемы разработчику клиента всё равно нужно написать код работы с ошибкой. Это, однако, не так; есть очень большая разница в написании кода для системы (3) по сравнению с (1) и (2): разработчику клиента *не надо знать* как устроена политика перезапросов сервера. Он может быть уверен, что сервер уже сам выполнил необходимые действия, и нет никакого смысла немедленно повторять запрос. Все серверные методы с этой точки зрения для клиента начинают выглядеть одинаково, а значит, эту функциональность (ожидание перезапроса, таймауты) можно передать на уровень фреймворка.
|
||||
|
||||
Важно, что для разработчика клиента при правильно работающих фреймворках (клиентском и серверном) пропадают ситуации неопределённости: ему не надо предусматривать какую-то логику «восстановления», когда запрос вроде бы не прошёл, но его ещё можно попытаться исправить. Клиент-серверное взаимодействие становится бинарным — или успех, или ошибка, а все пограничные градации обрабатываются другим кодом.
|
||||
|
||||
В итоге, более сложная архитектура оказался разделена по уровням ответственности, и каждый разработчик занимается своим делом. Разработчик гейтвея гарантирует наиболее оптимальный роутинг внутри дата-центра, разработчик фреймворка предоставляет функциональность по реализации политики таймаутов и перезапросов, а разработчик клиента пишет *бизнес-логику* обработки ошибок, а не код восстановления из низкоуровневых состояний неопределённости.
|
||||
|
||||
**Разумеется** все подобные оптимизации можно выполнить и без опоры на стандартную номенклатуру методов / статусов / заголовков HTTP, или даже вовсе поверх другого протокола. Достаточно разработать одинаковый формат данных, содержащий нужную мета-информацию, и научить промежуточные агенты и фреймворки его читать. В общем-то, именно это Филдинг и утверждает в своей диссертации. Но, конечно, очень желательно, чтобы этот код уже был кем-то написан за вас.
|
||||
|
||||
Заметим, что вышесказанное не имеет абсолютно ничего общего с многочисленными советами «как правильно разрабатывать REST API», которые можно найти в интернете:
|
||||
* неважно, есть ли в ваших URL глаголы — важно, являются ли они достаточно машиночитаемыми, чтобы правильно устроить роутинг;
|
||||
* неважно, хорошо ли HTTP-глагол подходит для описания того, что происходит с ресурсом — важно, что он указывает, является ли операция (не)модифицирующей, (не)кэшируемой, (не)идемпотентной; вам может казаться, что `DELETE /list?element_index=3` прекрасно описывает ваше намерение удалить третий элемент списка, но эта операция неидемпотентна, и использовать здесь метод `DELETE` нельзя;
|
||||
* сведение всего API к набору CRUD-операций не имеет ничего общего с парадигмой REST; просто разрабатывайте их сигнатуры, держа в голове принципы stateless и многослойности.
|
||||
|
||||
1. «Не используйте в URL глаголы, только существительные» — этот совет является всего лишь костылём для того, чтобы добиться правильной организации мета-информации об операции. В контексте работы с URL важно добиться двух моментов:
|
||||
* чтобы URL был ключом кэширования для кэшируемых операций и ключом идемпотентности для идемпотентных;
|
||||
* чтобы при маршрутизации запроса в многослойной системе можно было легко понять, куда будет маршрутизироваться эта операция по её URL — как машине, так и человеку.
|
||||
|
||||
2. «Используйте HTTP-глаголы для описания действий того, что происходит с ресурсом» — это правило попросту ставит телегу впереди лошади. Глагол указывает всем промежуточным агентам, является ли операция (не)модифицирующей, (не)кэшируемой, (не)идемпотентной; вместо того, чтобы выбирать строго по этим трём критериям, предлагается воспользоваться какой-то мнемоникой — если глагол подходит к смыслу операции, то и ок. Это в некоторых случаях просто опасно: вам может показаться, что `DELETE /list?element_index=3` прекрасно описывает ваше намерение удалить третий элемент списка, но т.к. эта операция неидемпотентна, использовать метод `DELETE` здесь нельзя;
|
||||
|
||||
3. «Используйте `POST` для создания сущностей, `GET` для доступа к ним, `PUT` для полной перезаписи, `PATCH` для частичной и `DELETE` для удаления» — вновь мнемоника, позволяющая «на пальцах» прикинуть, какие побочные эффекты возможны у какого из методов. Если попытаться разобраться в вопросе глубже, то получится, что вообще-то этот совет находится где-то между «бесполезен» и «вреден»:
|
||||
* использовать метод `GET` в API имеет смысл тогда и только тогда, когда вы можете указать заголовки кэширования; если выставить `Cache-Control` в `no-cache` — то получится просто неявный `POST`; если их не указать совсем, то какой-то промежуточный агент может взять и додумать их за вас;
|
||||
* создание сущностей желательно делать идемпотентным, в идеале — за `PUT` (например, через [схему с драфтами](#chapter-11-paragraph-13));
|
||||
* частичная перезапись через `PATCH` опасная и двусмысленная операция, лучше её [декомпозировать через более простые `PUT`](#chapter-11-paragraph-12);
|
||||
* наконец, в современных системах сущности очень редко удаляются — скорее архивируются или помечаются скрытыми, так что и здесь `PUT /archive?entity_id` будет уместнее.
|
||||
|
||||
4. «Не используйте вложенные ресурсы» — это правило просто отражает тот факт, что отношения между сущностями имеют тенденцию меняться, и строгие иерархии перестают быть строгими.
|
||||
|
||||
5. «Используйте множественное число для сущностей», «приписывайте слэш в конце URL» и тому подобные советы по стилистике кода, не имеющие никакого отношения к REST.
|
||||
|
||||
Осмелимся в конце этого раздела сформулировать четыре правила, которые позволят вам написать хорошее REST API:
|
||||
|
||||
1. Соблюдайте стандарт HTTP, *особенно* в части семантики методов, статусов и заголовков.
|
||||
2. Рассматривайте URL как ключ кэша и ключ идемпотентности.
|
||||
3. Проектируйте систему так, что она использует части URL (хост, путь, query-параметры), статусы и заголовки как метаинформацию, предназначенную для маршрутизации запроса внутри многослойной системы, возможно, с частичной перезаписью на каждом слое.
|
||||
4. Читайте сигнатуры вызовов HTTP-методов вашего API как код, и применяйте к нему те же стилистические правила, что и к обычному коду: сигнатуры должны быть семантичными, консистентными и читабельными.
|
||||
|
||||
#### Преимущества и недостатки REST
|
||||
|
||||
Главное преимущество, которое вам предоставляет REST — возможность положиться на то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать мета-информацию запроса и выполнять какие-то действия с её использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать и так далее — без необходимости писать какой-то дополнительный код. Немаловажно уточнить, что, если вы этими преимуществами не пользуетесь, никакой REST вам не нужен.
|
||||
|
||||
Главным недостатком REST является то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать мета-информацию запроса и выполнять какие-то действия с её использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать и так далее — даже если вы их об этом не просили. Более того, так как стандарты HTTP являются сложными, концепция REST — непонятной, а разработчики программного обеспечения — неидеальными, то промежуточные агенты могут трактовать метаданные запроса *неправильно*. Особенно это касается каких-то экзотических и сложных в имплементации стандартов.
|
||||
|
||||
Разработка распределённых систем в парадигме REST — это всегда некоторый торг: какую функциональность вы готовы отдать на откуп чужому коду, а какую — нет. Увы, нащупывать баланс приходится методом проб и ошибок.
|
||||
|
||||
#### О метапрограммировании и REST по Филдингу
|
||||
|
||||
Отдельно всё-таки выскажемся о трактовке REST по Филдингу-2008, которая, на самом деле, уходит корнями в распространённую концепцию [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS). С одной стороны, она является довольно логичным продолжением концепций, изложенных выше: если машиночитаемыми будут не только метаданные текущей исполняемой операции, но и всех возможных операций над ресурсом, это, конечно, позволит построить гораздо более функциональные сетевые агенты. Вообще сама идея метапрограммирования, когда клиент является настолько сложной вычислительной машиной, что способен расширять сам себя без необходимости привлечь разработчика, который прочитает документацию API и напишет код работы с ним, конечно, выглядит весьма привлекательной для любого технократа.
|
||||
|
||||
Недостатком этой идеи является тот факт, что клиент будет расширять сам себя без привлечения разработчика, который прочитает документацию API и напишет код работы с ним. Возможно, в идеальном мире так работает; в реальном — нет. Любое большое API неидеально, в нём всегда есть концепции, для понимания которых (пока что) требуется живой человек. А поскольку, повторимся, API работает мультипликатором и ваших возможностей, и ваших ошибок, автоматизированное метапрограммирование роботом поверх API чревато очень-очень большими ошибками.
|
||||
|
||||
Пока сильный ИИ не разработан, мы всё-таки настаиваем на том, что код работы с API должен писать живой человек, который опирается на подробную документацию, а не догадки о смысле гиперссылок в ответе сервера.
|
Loading…
x
Reference in New Issue
Block a user