mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-05-31 22:09:37 +02:00
clarifications
This commit is contained in:
parent
df9453b379
commit
895a38f86b
@ -4,9 +4,13 @@
|
||||
|
||||
Так сложилось, что в настоящий момент сетевой стек, используемый для разработки распределённых API, практически полностью унифицирован в двух точках. Одна из них — это Internet protocol suite, состоящий из базового протокола IP и надстройки в виде TCP или UDP над ним. На сегодняшний день альтернативы TCP/IP используются в чрезвычайно ограниченном спектре задач, и средний разработчик практически не сталкивается ни с каким другим сетевым стеком. Однако у TCP/IP с прикладной точки зрения есть существенный недостаток — он оперирует поверх системы IP-адресов, которые плохо подходят для организации распределённых систем:
|
||||
* во-первых, люди не запоминают IP-адреса и предпочитают оперировать «говорящими» именами;
|
||||
* во-вторых, IP-адрес является технической сущностью, связанной с узлом сети, а разработчики приложений хотели бы иметь возможность добавлять и изменять узлы, не нарушая работы своих приложений.
|
||||
* во-вторых, IP-адрес является технической сущностью, связанной с узлом сети, а разработчики хотели бы иметь возможность добавлять и изменять узлы, не нарушая работы своих приложений.
|
||||
|
||||
Удобной (и опять же имеющей почти стопроцентное проникновение) абстракцией над IP-адресами оказалась система доменных имён, позволяющий назначить узлам сети человекочитаемые синонимы и манипулировать ими по усмотрению разработчика. Появление доменных имён потребовало разработки клиент-серверных протоколов более высокого, чем TCP/IP, уровня, и для передачи текстовых (гипертекстовых) данных таким протоколом стал [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html), разработанный Тимом Бёрнерсом-Ли опубликованный в 1991 году. Протокол был очень прост и всего лишь описывал способ получить документ, открыв TCP/IP соединение с сервером и передав строку вида `GET адрес_документа`. Позднее протокол был дополнен стандартом URL, позволяющим детализировать адрес документа, и далее протокол начал развиваться стремительно: появились новые глаголы помимо `GET`, статусы ответов, заголовки, типы данных и так далее.
|
||||
Удобной (и опять же имеющей почти стопроцентное проникновение) абстракцией над IP-адресами оказалась система доменных имён, позволяющий назначить узлам сети человекочитаемые синонимы.
|
||||
|
||||
Появление доменных имён потребовало разработки клиент-серверных протоколов более высокого, чем TCP/IP, уровня, и для передачи текстовых (гипертекстовых) данных таким протоколом стал [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html), разработанный Тимом Бёрнерсом-Ли опубликованный в 1991 году. Помимо поддержки обращения к узлам сети по именам, HTTP также предоставил ещё одну очень удобную абстракцию, а именно назначение собственных адресов подсемейству API, работающих на одном сетевом узле.
|
||||
|
||||
Протокол был очень прост и всего лишь описывал способ получить документ, открыв TCP/IP соединение с сервером и передав строку вида `GET адрес_документа`. Позднее протокол был дополнен стандартом URL, позволяющим детализировать адрес документа, и далее протокол начал развиваться стремительно: появились новые глаголы помимо `GET`, статусы ответов, заголовки, типы данных и так далее.
|
||||
|
||||
HTTP появился изначально для передачи размеченного гипертекста, что для программных интерфейсов подходит слабо. Однако HTML быстро эволюционировал в более строгий и машиночитаемый XML, который быстро стал одним из общепринятых форматов описания вызовов API. С начала 2000-х XML начал вытесняться более простым и интероперабельным JSON, и сегодня говоря о HTTP API, чаще всего имеют в виду такие интерфейсы, в которых данные передаются в формате JSON по протоколу HTTP.
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
* разработка REST API должна фокусироваться на описании медиатипов, представляющих ресурсы; при этом клиент вообще ничего про эти медиатипы знать не должен;
|
||||
* в REST API не должно быть фиксированных имён ресурсов и операций над ними, клиент должен извлекать эту информацию из ответов сервера.
|
||||
|
||||
REST по Филдингу-2008 подразумевает, что клиент, получив каким-то образом ссылку на точку входа в REST API, далее должен быть в состоянии полностью выстроить взаимодействие с API, не обладая вообще никаким априорным знанием о нём, и уж тем более не должен содержать никакого специально написанного кода для работы с этим API. Это требование — гораздо более сильное, нежели принципы, описанные в диссертации 2000 года. В частности, из идеи REST-2008 вытекает отсутствие фиксированных шаблонов URL для выполнения операций над ресурсами — предполагается, что такие URL присутствуют в виде гиперссылок в представлениях ресурсов. Диссертация же 2000 года никаких строгих определений «гипермедиа», которые препятствовали бы идее конструирования ссылок на основе априорных знаний об API (например, по спецификации), не содержит.
|
||||
REST по Филдингу-2008 подразумевает, что клиент, получив каким-то образом ссылку на точку входа в REST API, далее должен быть в состоянии полностью выстроить взаимодействие с API, не обладая вообще никаким априорным знанием о нём, и уж тем более не должен содержать никакого специально написанного кода для работы с этим API. Это требование — гораздо более сильное, нежели принципы, описанные в диссертации 2000 года. В частности, из идеи REST-2008 вытекает отсутствие фиксированных шаблонов URL для выполнения операций над ресурсами — предполагается, что такие URL присутствуют в виде гиперссылок в представлениях ресурсов (эта концепция известна также под названием [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS)). Диссертация же 2000 года никаких строгих определений «гипермедиа», которые препятствовали бы идее конструирования ссылок на основе априорных знаний об API (например, по спецификации), не содержит.
|
||||
|
||||
**NB**: оставляя за скобками тот факт, что Филдинг весьма вольно истолковал свою собственную диссертацию, просто отметим, что ни одна существующая система в мире не удовлетворяет описанию REST по Филдингу-2008.
|
||||
|
||||
|
@ -72,9 +72,7 @@ URL принято раскладывать на составляющие, ка
|
||||
|
||||
##### HTTP-глаголы
|
||||
|
||||
Важнейшая составляющая HTTP запроса — это глагол (метод), описывающий операцию, применяемую к ресурсу. При разработке HTTP API, как правило, стремятся к тому, чтобы сигнатура `ГЛАГОЛ /url` была максимально читабельна и соответствовала семантике операции над ресурсом.
|
||||
|
||||
RFC 9110 описывает восемь глаголов (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `CONNECT`, `OPTIONS` и `TRACE`), из которых нас как разработчиков API интересует первые четыре. `CONNECT`, `OPTIONS` и `TRACE` — технические методы, которые очень редко используются в HTTP API (за исключением `OPTIONS`, который необходимо реализовать, если необходим доступ к API из браузера). Теоретически, `HEAD` (метод получения *только метаданных*, то есть заголовков, ресурса) мог бы быть весьма полезен в HTTP API, но по неизвестным нам причинам практически в этом смысле не используется.
|
||||
Важнейшая составляющая HTTP запроса — это глагол (метод), описывающий операцию, применяемую к ресурсу. RFC 9110 описывает восемь глаголов (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `CONNECT`, `OPTIONS` и `TRACE`), из которых нас как разработчиков API интересует первые четыре. `CONNECT`, `OPTIONS` и `TRACE` — технические методы, которые очень редко используются в HTTP API (за исключением `OPTIONS`, который необходимо реализовать, если необходим доступ к API из браузера). Теоретически, `HEAD` (метод получения *только метаданных*, то есть заголовков, ресурса) мог бы быть весьма полезен в HTTP API, но по неизвестным нам причинам практически в этом смысле не используется.
|
||||
|
||||
Помимо RFC 9110, множество других RFC предлагают использовать дополнительные HTTP-глаголы (такие, например, как `COPY`, `LOCK`, `SEARCH` — полный список можно найти [здесь](http://www.iana.org/assignments/http-methods/http-methods.xhtml)), однако из всего разнообразия предложенных стандартов лишь один имеет широкое хождение — метод `PATCH`. Причины такого положения дел довольно тривиальны — этих пяти методов (`GET`, `POST`, `PUT`, `DELETE`, `PATCH`) достаточно для почти любого HTTP API.
|
||||
|
||||
|
@ -33,6 +33,8 @@ HTTP/1.1 200 OK
|
||||
|
||||
Если попытаться сформулировать главный принцип разработки HTTP API, то мы получим примерно следующее: **лучше бы ты разрабатывал API так, чтобы промежуточные агенты могли читать и интерпретировать мета-информацию запроса и ответа**.
|
||||
|
||||
Помимо прозрачности протокола есть и некоторые семантические отличия HTTP API от RPC-фреймворков. При разработке HTTP API, как правило, стремятся к тому, чтобы сигнатура `ГЛАГОЛ /url` была максимально читабельна и соответствовала семантике операции над ресурсом, т.е. фактически была эквивалента вызову операции `ГЛАГОЛ` на некоторой инстанции статического объекта `/url`. (RPC-парадигма, напротив, подразумевает, что URL — это имя функции, которая вызывается с передачей параметров действия в query/теле запроса.) Отсюда проистекает популярное самопровозглашённое требование к URL в рамках HTTP API быть существительными, т.к. фактически они эквивалентны переменным в коде. (Мы не видим в этом требовании большого смысла, и не придерживаемся его в этой книге строго — как и в случае имён переменных, нам требуется в первую очередь удобство и понятность, а не бездумное следование правилу. Однако принцип максимизации читабельности конструкции `ГЛАГОЛ /url` мы, разумеется, всячески приветствуем.)
|
||||
|
||||
В отличие от большинства альтернативных технологий, основной импульс развития которых исходит от какой-то одной крупной IT-компании (Facebook, Google, Apache Software Foundation), инструменты для HTTP API разрабатываются множеством различных компаний и коллабораций. Соответственно, вместо гомогенного, но ограниченного в возможностях фреймворка, для HTTP API мы имеем множество самых разных инструментов, таких как:
|
||||
* различные прокси и API-гейтвеи (nginx, Envoy);
|
||||
* различные форматы описания спецификаций (в первую очередь, OpenAPI) и связанные инструменты для работы со спецификациями (Redoc, Swagger UI) и кодогенерации;
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
Все эти варианты семантически вполне допустимы и в общем-то равноправны. Более того, можно даже поддерживать несколько из них одновременно.
|
||||
|
||||
2. Насколько строго должна выдерживаться читабельность операции `ГЛАГОЛ ресурс`? Многие учебники по REST API безапелляционно требуют, чтобы `ресурс` был существительным (ведь странно применять глагол к глаголу), и, таким образом, в примерах выше должно быть не `prepare`, а `preparation` (а вариант `/action=prepare&coffee_machine_id=<id>&recipe=lungo` вовсе недопустим, так как нет объекта действия). С нашей точки зрения единственная выгода от соблюдения этого требования только одна — становится интуитивно понятным требование «URL = ключ кэширования»; но мы, естественно, выступаем за то, чтобы разработчик *понимал* основы протокола, а не интуитивно догадывался о них.
|
||||
2. Насколько строго должна выдерживаться читабельность операции `ГЛАГОЛ ресурс`? Многие учебники по REST API безапелляционно требуют, чтобы `ресурс` был существительным (ведь странно применять глагол к глаголу), и, таким образом, в примерах выше должно быть не `prepare`, а `preparator` (а вариант `/action=prepare&coffee_machine_id=<id>&recipe=lungo` вовсе недопустим, так как нет объекта действия). С нашей точки зрения единственная выгода от соблюдения этого требования только одна — становится интуитивно понятным требование «URL = ключ кэширования»; но мы, естественно, выступаем за то, чтобы разработчик *понимал* основы протокола, а не интуитивно догадывался о них.
|
||||
|
||||
3. Если сигнатура вызова по умолчанию модифицирующая или неидемпотентная, означает ли это, что операция *обязана* быть модифицирующей / идемпотентной? Двойственность смысловой нагрузки глаголов (семантика vs побочные действия) порождает неопределённость в вопросах организации таких API. Рассмотрим, например, ресурс `/v1/search`, осуществляющий поиск предложений кофе в нашем учебном API. С каким глаголом мы должны к нему обращаться?
|
||||
* С одной стороны, `GET /v1/search?query=<поисковый запрос>` позволяет явно продекларировать, что никаких посторонних эффектов у этого запроса нет (никакие данные не перезаписываются) и результаты его можно кэшировать (при условии, что все значимые параметры передаются в URL, т.е. в виде path и query-параметров).
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
Простых ответов на вопросы выше у нас, к сожалению, нет (особенно если мы добавим к ним механики логирования и построения мониторингов по URL запроса). В рамках настоящей книги мы придерживаемся следующего подхода:
|
||||
|
||||
* сигнатура вызова в первую очередь должна быть лаконична и читабельна; усложение сигнатур в угоду абстрактным концепциям нежелательно;
|
||||
* сигнатура вызова в первую очередь должна быть лаконична и читабельна; усложнение сигнатур в угоду абстрактным концепциям нежелательно;
|
||||
* иерархия ресурсов выдерживается там, где она однозначна (т.е., если сущность низшего уровня абстракции однозначно подчинена сущности высшего уровня абстракции, то отношения между ними будут выражены в виде вложенных путей);
|
||||
* если есть сомнения в том, что иерархия в ходе дальнейшего развития API останется неизменной, лучше завести новый верхнеуровневый префикс, а не вкладывать новые сущности в уже существующие;
|
||||
* семантика HTTP-глагола приоритетнее ложного предупреждения о небезопасности/неидемпотентности (в частности, если операция является безопасной, но ресурсозатратной, с нашей точки зрения вполне разумно использовать метод `POST` для индикации этого факта);
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
2. Не пренебрегайте стандартными заголовками — `Date`, `Content-Type`, `Content-Encoding`, `Content-Length`, `Cache-Control`, `Retry-After` — и вообще старайтесь не полагать на то, что клиент правильно догадывается о параметрах по умолчанию.
|
||||
|
||||
3. Поддержите метод `OPTIONS` на случай, если ваш API захотят использовать из браузеров.
|
||||
3. Поддержите метод `OPTIONS` и [протокол CORS](https://fetch.spec.whatwg.org/#cors-protocol) на случай, если ваш API захотят использовать из браузеров.
|
||||
|
||||
4. Определитесь с правилами выбора кейсинга параметров (и преобразований кейсинга при перемещении параметра между различными частями запроса) и придерживайтесь их.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user