You've already forked The-API-Book
mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-08-10 21:51:42 +02:00
refactoring
This commit is contained in:
264
docs/API.ru.html
264
docs/API.ru.html
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
|||||||
"author": "Sergey Konstantinov",
|
"author": "Sergey Konstantinov",
|
||||||
"chapter": "Chapter",
|
"chapter": "Chapter",
|
||||||
"toc": "Table of Contents",
|
"toc": "Table of Contents",
|
||||||
"description": "API-first development is one of the hottest technical topics nowadays since many companies have started to realize that APIs serves as a multiplier to their opportunities — but it amplifies the design mistakes as well. This book is written to share expertise and describe best practices in designing and developing APIs. It comprises six sections dedicated to the following topics: the API design, API patterns, maintaining backward compatibility, HTTP APIs & REST, SDKs and UI libraries, API product management.",
|
"description": "API-first development is one of the hottest technical topics nowadays since many companies have started to realize that APIs serves as a multiplier to their opportunities — but it amplifies the design mistakes as well. This book is written to share expertise and describe best practices in designing and developing APIs. It comprises six sections dedicated to the following topics: the API design, API patterns, maintaining backward compatibility, HTTP APIs & REST architectural principles, SDKs and UI libraries, API product management.",
|
||||||
"publisher": "Sergey Konstantinov",
|
"publisher": "Sergey Konstantinov",
|
||||||
"copyright": "© Sergey Konstantinov, 2023",
|
"copyright": "© Sergey Konstantinov, 2023",
|
||||||
"locale": "en_US",
|
"locale": "en_US",
|
||||||
@@ -66,7 +66,8 @@
|
|||||||
"mediumHref": "https://twirl.medium.com/",
|
"mediumHref": "https://twirl.medium.com/",
|
||||||
"mediumTag": "Medium",
|
"mediumTag": "Medium",
|
||||||
"substackHref": "https://twirl.substack.com/",
|
"substackHref": "https://twirl.substack.com/",
|
||||||
"substackTag": "Substack"
|
"substackTag": "Substack",
|
||||||
|
"substackString": "twirl.substack.com"
|
||||||
},
|
},
|
||||||
"sourceCodeAt": "Source code available at",
|
"sourceCodeAt": "Source code available at",
|
||||||
"frontPage": {
|
"frontPage": {
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
Протокол был очень прост и всего лишь описывал способ получить документ, открыв TCP/IP соединение с сервером и передав строку вида `GET адрес_документа`. Позднее протокол был дополнен стандартом URL, позволяющим детализировать адрес документа, и далее протокол начал развиваться стремительно: появились новые глаголы помимо `GET`, статусы ответов, заголовки, типы данных и так далее.
|
Протокол был очень прост и всего лишь описывал способ получить документ, открыв TCP/IP соединение с сервером и передав строку вида `GET адрес_документа`. Позднее протокол был дополнен стандартом URL, позволяющим детализировать адрес документа, и далее протокол начал развиваться стремительно: появились новые глаголы помимо `GET`, статусы ответов, заголовки, типы данных и так далее.
|
||||||
|
|
||||||
HTTP появился изначально для передачи размеченного гипертекста, что для программных интерфейсов подходит слабо. Однако HTML быстро эволюционировал в более строгий и машиночитаемый XML, который быстро стал одним из общепринятых форматов описания вызовов API. С начала 2000-х XML начал вытесняться более простым и интероперабельным JSON, и сегодня говоря о HTTP API, чаще всего имеют в виду такие интерфейсы, в которых данные передаются в формате JSON по протоколу HTTP.
|
HTTP появился изначально для передачи размеченного гипертекста, что для программных интерфейсов подходит слабо. Однако HTML со временем эволюционировал в более строгий и машиночитаемый XML, который быстро стал одним из общепринятых форматов описания вызовов API. С начала 2000-х XML начал вытесняться более простым и интероперабельным JSON, и сегодня, говоря об HTTP API, чаще всего имеют в виду такие интерфейсы, в которых данные передаются в формате JSON по протоколу HTTP.
|
||||||
|
|
||||||
Поскольку, с одной стороны, HTTP был простым и понятным протоколом, позволяющим осуществлять произвольные запросы к удаленным серверам по их доменным именам, и, с другой стороны, быстро оброс почти бесконечным количеством разнообразных расширений над базовой функциональностью, он довольно быстро стал второй точкой, к которой сходятся сетевые технологии: практически все запросы к API внутри TCP/IP-сетей осуществляются по протоколу HTTP (и даже если используется альтернативный протокол, запросы в нём всё равно зачастую оформлены в виде HTTP-пакетов просто ради удобства). При этом, однако, в отличие от TCP/IP-уровня, каждый разработчик сам для себя решает, какой объём функциональности, предоставляемой протоколом и многочисленными расширениями к нему, он готов применить. В частности, gRPC и GraphQL работают поверх HTTP, но используют крайне ограниченное подмножество его возможностей.
|
Поскольку, с одной стороны, HTTP был простым и понятным протоколом, позволяющим осуществлять произвольные запросы к удаленным серверам по их доменным именам, и, с другой стороны, быстро оброс почти бесконечным количеством разнообразных расширений над базовой функциональностью, он довольно быстро стал второй точкой, к которой сходятся сетевые технологии: практически все запросы к API внутри TCP/IP-сетей осуществляются по протоколу HTTP (и даже если используется альтернативный протокол, запросы в нём всё равно зачастую оформлены в виде HTTP-пакетов просто ради удобства). При этом, однако, в отличие от TCP/IP-уровня, каждый разработчик сам для себя решает, какой объём функциональности, предоставляемой протоколом и многочисленными расширениями к нему, он готов применить. В частности, gRPC и GraphQL работают поверх HTTP, но используют крайне ограниченное подмножество его возможностей.
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ HTTP появился изначально для передачи размеч
|
|||||||
* протоколом взаимодействия является HTTP версий 1.1 и выше;
|
* протоколом взаимодействия является HTTP версий 1.1 и выше;
|
||||||
* форматом данных является JSON (за исключением эндпойнтов, специально предназначенных для передачи данных, как правило, файлов, в других форматах);
|
* форматом данных является JSON (за исключением эндпойнтов, специально предназначенных для передачи данных, как правило, файлов, в других форматах);
|
||||||
* в качестве идентификаторов ресурсов используется URL в соответствии со стандартом;
|
* в качестве идентификаторов ресурсов используется URL в соответствии со стандартом;
|
||||||
* семантика вызовов HTTP-эндпойнтов соответствует спецификации
|
* семантика вызовов HTTP-эндпойнтов соответствует спецификации;
|
||||||
* никакие из веб-стандартов нигде не нарушается специально.
|
* никакие из веб-стандартов нигде не нарушается специально.
|
||||||
|
|
||||||
Такое API мы будем для краткости называть просто «HTTP API» или «JSON-over-HTTP API». Мы понимаем, что такое использование терминологически некорректно, но писать каждый раз «JSON-over-HTTP эндпойнты, утилизирующие семантику, описанную в стандартах HTTP и URL» не представляется возможным.
|
Такое API мы будем для краткости называть просто «HTTP API» или «JSON-over-HTTP API». Мы понимаем, что такое использование терминологически некорректно, но писать каждый раз «JSON-over-HTTP эндпойнты, утилизирующие семантику, описанную в стандартах HTTP и URL» не представляется возможным.
|
@@ -1,6 +1,6 @@
|
|||||||
### [Преимущества и недостатки HTTP API][http-api-pros-and-cons]
|
### [Преимущества и недостатки HTTP API. Сравнение с альтернативными технологиями][http-api-pros-and-cons]
|
||||||
|
|
||||||
После трёх вступительных глав с прояснением основных терминов и понятий (такова, увы, цена популярности технологии) у читателя может возникнуть резонный вопрос — а почему вообще существует такая дихотомия: какие-то API полагаются на стандартную семантику HTTP, а какие-то полностью от неё отказываются в пользу новоизобретённых стандартов. Например, если мы посмотрим на [формат ответа в JSON-RPC](https://www.jsonrpc.org/specification#response_object), то мы обнаружим, что он легко мог бы быть заменён на стандартные средства протокола HTTP. Вместо
|
По прочтению предыдущей главы у читателя может возникнуть резонный вопрос — а почему вообще существует такая дихотомия: одни API полагаются на стандартную семантику HTTP, другие полностью от неё отказываются в пользу новоизобретённых стандартов, а третьи существуют где-то посередине. Например, если мы посмотрим на [формат ответа в JSON-RPC](https://www.jsonrpc.org/specification#response_object), то мы обнаружим, что он легко мог бы быть заменён на стандартные средства протокола HTTP. Вместо
|
||||||
|
|
||||||
```
|
```
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
@@ -17,36 +17,47 @@ HTTP/1.1 200 OK
|
|||||||
|
|
||||||
сервер мог бы ответить просто `400 Bad Request` (с передачей идентификатора запроса, ну скажем, в заголовке `X-OurCoffeeAPI-RequestId`). Тем не менее, разработчики протокола посчитали нужным разработать свой собственный формат.
|
сервер мог бы ответить просто `400 Bad Request` (с передачей идентификатора запроса, ну скажем, в заголовке `X-OurCoffeeAPI-RequestId`). Тем не менее, разработчики протокола посчитали нужным разработать свой собственный формат.
|
||||||
|
|
||||||
Такая ситуация (не только конкретно с JSON-RPC, а почти со всеми высокоуровневыми протоколами поверх HTTP) сложилась по множеству причин, включая разнообразные исторические (например, невозможность использовать многие возможности HTTP из ранних реализаций `XMLHttpRequest` в браузерах). Однако, новые варианты RPC-протоколов, использующих абсолютный минимум возможностей HTTP, продолжают появляться и сегодня.
|
Такая ситуация (не только конкретно с JSON-RPC, а почти со всеми высокоуровневыми протоколами поверх HTTP) сложилась по множеству причин, включая разнообразные исторические (например, невозможность использовать многие возможности HTTP из ранних реализаций `XMLHttpRequest` в браузерах). Однако, новые варианты RPC-протоколов, использующих абсолютный минимум возможностей HTTP, продолжают появляться и сегодня. Мы можем попытаться выделить по крайней мере четыре группы причин, приводящих к такому размежеванию.
|
||||||
|
|
||||||
Чтобы разрешить этот парадокс, обратим внимание на одно принципиальное различие между использованием протоколов уровня приложения (как в нашем примере с JSON-RPC) и чистого HTTP: если ошибка `400 Bad Request` является прозрачной для практически любого сетевого агента, то ошибка в собственном формате JSON-RPC таковой не является — во-первых, потому что понять её может только агент с поддержкой JSON-RPC, а, во-вторых, что более важно, в JSON-RPC статус запроса *не является метаинформацией*. Протокол HTTP позволяет прочитать такие детали, как метод и URL запроса, статус операции, заголовки запроса и ответа, *не читая тело запроса целиком*. Для большинства протоколов более высокого уровня, включая JSON-RPC, это не так: даже если агент и обладает поддержкой протокола, ему необходимо прочитать и разобрать тело ответа.
|
##### Машиночитаемость метаданных
|
||||||
|
|
||||||
Каким образом эта самая возможность читать метаданные нам может быть полезна? Современный стек взаимодействия между клиентом и сервером является (как и предсказывал Филдинг) многослойным. Мы можем выделить множество агентов разного уровня, которые, так или иначе, обрабатывают сетевые запросы и ответы:
|
Обратим внимание на принципиальное различие между использованием протоколов уровня приложения (как в нашем примере с JSON-RPC) и чистого HTTP. В примере выше ошибку `400 Bad Request` может прочитать практически любой сетевой агент, если мы используем чистый HTTP; если же мы используем собственный формат JSON-RPC, ошибка прозрачной не является — во-первых, потому что понять её может только агент с поддержкой JSON-RPC, а, во-вторых, что более важно, в JSON-RPC статус запроса *не является метаинформацией*. Протокол HTTP позволяет прочитать такие детали, как метод и URL запроса, статус операции, заголовки запроса и ответа, *не читая тело запроса или ответа целиком*. Для большинства протоколов более высокого уровня, включая JSON-RPC, это не так: даже если агент и обладает поддержкой протокола, ему необходимо прочитать и разобрать тело ответа.
|
||||||
|
|
||||||
|
Каким образом эта самая возможность читать метаданные может быть полезна? Современный стек взаимодействия между клиентом и сервером является многослойным. Мы можем выделить множество агентов разного уровня, которые, так или иначе, обрабатывают сетевые запросы и ответы:
|
||||||
* разработчик пишет код поверх какого-то фреймворка, который отправляет запросы;
|
* разработчик пишет код поверх какого-то фреймворка, который отправляет запросы;
|
||||||
* фреймворк базируется на API языка программирования, компилятор или интерпретатор которого, в свою очередь, полагается на API операционной системы;
|
* фреймворк базируется на API языка программирования, компилятор или интерпретатор которого, в свою очередь, полагается на API операционной системы;
|
||||||
* запрос доходит до сервера, возможно, через промежуточные HTTP-прокси;
|
* запрос доходит до сервера, возможно, через промежуточные HTTP-прокси;
|
||||||
* сервер, в свою очередь, тоже представляет собой несколько слоёв абстракции в виде фреймворка, языка программирования и ОС;
|
* сервер, в свою очередь, тоже представляет собой несколько слоёв абстракции в виде фреймворка, языка программирования и ОС;
|
||||||
* перед конечным сервером, как правило, находится веб-сервер, проксирующий запрос, а зачастую и не один;
|
* перед конечным обработчиком запроса, как правило, находится веб-сервер, проксирующий запрос, а зачастую и не один;
|
||||||
* в современных облачных архитектурах HTTP-запрос, прежде чем дойти до конечного обработчика, пройдёт через несколько абстракций в виде прокси и гейтвеев.
|
* в современных облачных архитектурах HTTP-запрос, прежде чем дойти до конечного обработчика, пройдёт через несколько абстракций в виде прокси и гейтвеев.
|
||||||
|
|
||||||
Главное преимущество, которое предоставляет следование букве стандарта HTTP — возможность положиться на то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать метаданные запроса и выполнять какие-то действия с их использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать, проксировать и так далее — без необходимости писать какой-то дополнительный код.
|
Главное преимущество, которое предоставляет следование букве стандарта HTTP — возможность положиться на то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать метаданные запроса и выполнять какие-то действия с их использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать, проксировать и так далее — без необходимости писать какой-то дополнительный код. Если попытаться сформулировать главный принцип разработки HTTP API, то мы получим примерно следующее: **лучше бы ты разрабатывал API так, чтобы промежуточные агенты могли читать и интерпретировать метаданные запроса и ответа**.
|
||||||
|
|
||||||
Если попытаться сформулировать главный принцип разработки HTTP API, то мы получим примерно следующее: **лучше бы ты разрабатывал API так, чтобы промежуточные агенты могли читать и интерпретировать мета-информацию запроса и ответа**.
|
|
||||||
|
|
||||||
В отличие от большинства альтернативных технологий, основной импульс развития которых исходит от какой-то одной крупной IT-компании (Facebook, Google, Apache Software Foundation), инструменты для HTTP API разрабатываются множеством различных компаний и коллабораций. Соответственно, вместо гомогенного, но ограниченного в возможностях фреймворка, для HTTP API мы имеем множество самых разных инструментов, таких как:
|
|
||||||
* различные прокси и API-гейтвеи (nginx, Envoy);
|
|
||||||
* различные форматы описания спецификаций (в первую очередь, OpenAPI) и связанные инструменты для работы со спецификациями (Redoc, Swagger UI) и кодогенерации;
|
|
||||||
* ПО для разработчиков, позволяющее удобным образом разрабатывать и отлаживать клиенты API (Postman, Insomnia) и так далее.
|
|
||||||
|
|
||||||
Конечно, большинство этих инструментов применимы и для работы с API, реализующими альтернативные парадигмы. Однако именно способность промежуточных агентов считывать метаданные HTTP запросов позволяет легко строить сложные конвейеры типа экспортировать access-логи nginx в Prometheus и из коробки получить удобные мониторинги статус-кодов ответов в Grafana.
|
|
||||||
|
|
||||||
Отдельно заметим что HTTP API является на сегодняшний день выбором по умолчанию при разработке публичных API. В силу озвученных выше причин, как бы ни был устроен технологический стек партнёра, интегрироваться с HTTP API он сможет без особых усилий. При этом распространённость технологии понижает и порог входа, и требования к квалификации инженеров партнёра.
|
|
||||||
|
|
||||||
Главным недостатком HTTP API является то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать метаданные запроса и выполнять какие-то действия с их использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать, проксировать и так далее — даже если вы их об этом не просили. Более того, так как стандарты HTTP являются сложными, концепция REST — непонятной, а разработчики программного обеспечения — неидеальными, то промежуточные агенты (и разработчики партнёра!) могут трактовать метаданные запроса *неправильно*. Особенно это касается каких-то экзотических и сложных в имплементации стандартов. Как правило, одной из причин разработки новых RPC-фреймворков декларируется стремление обеспечить простоту и консистентность работы с протоколом, чтобы таким образом уменьшить поле для потенциальных ошибок в реализации интеграции с API.
|
Главным недостатком HTTP API является то, что промежуточные агенты, от клиентских фреймворков до API-гейтвеев, умеют читать метаданные запроса и выполнять какие-то действия с их использованием — настраивать политику перезапросов и таймауты, логировать, кэшировать, шардировать, проксировать и так далее — даже если вы их об этом не просили. Более того, так как стандарты HTTP являются сложными, концепция REST — непонятной, а разработчики программного обеспечения — неидеальными, то промежуточные агенты (и разработчики партнёра!) могут трактовать метаданные запроса *неправильно*. Особенно это касается каких-то экзотических и сложных в имплементации стандартов. Как правило, одной из причин разработки новых RPC-фреймворков декларируется стремление обеспечить простоту и консистентность работы с протоколом, чтобы таким образом уменьшить поле для потенциальных ошибок в реализации интеграции с API.
|
||||||
|
|
||||||
Указанное выше соображение распространяется не только на программное обеспечение, но и на его создателей. Представление разработчиков о HTTP API, увы, также фрагментировано. Практически любой программист как-то умеет работать с HTTP API, но редко при этом знает стандарт или хотя бы консультируется с ним при написании кода. Это ведёт к тому, что добиться качественной и консистентной реализации логики работы с HTTP API может быть сложнее, нежели при использовании альтернативных технологий — причём это соображение справедливо как для партнёров-интеграторов, так и для самого провайдера API.
|
##### Качество решений
|
||||||
|
|
||||||
#### Вопросы производительности
|
Возможность читать и интерпретировать запросы приводит к широкой фрагментации доступных инструментов для работы с HTTP API. На рынке доступно (зачастую бесплатно) множество самых разных инструментов, таких как:
|
||||||
|
* различные прокси и API-гейтвеи (nginx, Envoy);
|
||||||
|
* различные форматы описания спецификаций (в первую очередь, OpenAPI) и связанные инструменты для работы со спецификациями (Redoc, Swagger UI) и кодогенерации;
|
||||||
|
* ПО для разработчиков, позволяющее удобным образом разрабатывать и отлаживать клиенты API (Postman, Insomnia);
|
||||||
|
* и так далее.
|
||||||
|
|
||||||
|
Конечно, большинство этих инструментов применимы и для работы с API, реализующими альтернативные парадигмы. Однако именно способность промежуточных агентов считывать метаданные HTTP запросов и одинаково их интерпретировать позволяет легко строить сложные конвейеры типа экспортировать access-логи nginx в Prometheus и из коробки получить удобные мониторинги статус-кодов ответов в Grafana.
|
||||||
|
|
||||||
|
Обратной стороной этой гибкости является качество самих решений и количество времени, необходимого на их интеграцию, особенно если ваш стек чем-то отличается от стандартного. В то же время основной импульс развития альтернативных технологий исходит от какой-то одной крупной IT-компании (Facebook, Google, Apache Software Foundation), которые предоставляют полный набор инструментов для работы со своим протоколом. Такой фреймворк может быть менее функциональным, но почти наверняка является более гомогенным и качественным в плане удобства разработки, поддержки пользователей и количества известных ошибок.
|
||||||
|
|
||||||
|
Соображения выше распространяются не только на программное обеспечение, но и на его создателей. Представление разработчиков о HTTP API, увы, также фрагментировано. Практически любой программист как-то умеет работать с HTTP API, но редко при этом досконально знает стандарт или хотя бы консультируется с ним при написании кода. Это ведёт к тому, что добиться качественной и консистентной реализации логики работы с HTTP API может быть сложнее, нежели при использовании альтернативных технологий — причём это соображение справедливо как для партнёров-интеграторов, так и для самого провайдера API.
|
||||||
|
|
||||||
|
Отдельно заметим что HTTP API является на сегодняшний день выбором по умолчанию при разработке публичных API. В силу озвученных выше причин, как бы ни был устроен технологический стек партнёра, интегрироваться с HTTP API он сможет без особых усилий. При этом распространённость технологии понижает и порог входа, и требования к квалификации инженеров партнёра.
|
||||||
|
|
||||||
|
##### Идеология разработки
|
||||||
|
|
||||||
|
Современные HTTP API унаследовали парадигму разработки ещё с тех времён, когда по протоколу HTTP в основном передавали гипертекст. В ней считается, что HTTP-запрос представляет собой операцию, выполняемую над некоторым объектом (ресурсом), который идентифицируется с помощью URL. Большинство альтернативных технологий придерживаются других парадигм; чаще всего URL в них идентифицирует *функцию*, которую необходимо выполнить с передачей указанных параметров. Подобная семантика не то чтобы противоречит HTTP — выполнение удалённых процедур хорошо описывается протоколом — но делает использование стандартных возможностей протокола бессмысленным (например, `Range`-заголовки) или вовсе опасным (возникает двусмысленность интерпретации, скажем, смысла заголовка `ETag`).
|
||||||
|
|
||||||
|
С точки зрения клиентской разработки следование парадигме HTTP часто требует реализации дополнительного слоя абстракции, который превращает вызов методов на объектах в HTTP-операции над нужными ресурсам. RPC-технологии в этом плане удобнее для интеграции. (Впрочем, любой достаточно сложный RPC API все равно потребует промежуточного уровня абстракции, а, например, GraphQL в нём нуждается изначально.)
|
||||||
|
|
||||||
|
##### Вопросы производительности
|
||||||
|
|
||||||
В пользу многих современных альтернатив HTTP API — таких как GraphQL, gRPC, Apache Thrift — часто приводят аргумент о низкой производительности JSON-over-HTTP API по сравнению с рассматриваемой технологией; конкретнее, называются следующие проблемы:
|
В пользу многих современных альтернатив HTTP API — таких как GraphQL, gRPC, Apache Thrift — часто приводят аргумент о низкой производительности JSON-over-HTTP API по сравнению с рассматриваемой технологией; конкретнее, называются следующие проблемы:
|
||||||
|
|
@@ -1,6 +1,6 @@
|
|||||||
### [Мифология REST][http-api-rest-myth]
|
### [Мифология REST][http-api-rest-myth]
|
||||||
|
|
||||||
Прежде, чем перейти непосредственно к паттернам проектирования HTTP API, мы должны сделать ещё одно терминологическое отступление. Очень часто API, соответствующие данному нами в предыдущей главе определению, называют «REST API» или «RESTful API». В настоящем разделе мы эти термины не используем, поскольку оба этих термина неформальные и не несут никакого конкретного смысла.
|
Прежде, чем перейти непосредственно к паттернам проектирования HTTP API, мы должны сделать ещё одно терминологическое отступление. Очень часто HTTP API, соответствующие данному нами в главе «[О концепции HTTP API и терминологии](#http-api-concepts)» определению, называют «REST API» или «RESTful API». В настоящем разделе мы эти термины не используем, поскольку оба этих термина неформальные и не несут конкретного смысла.
|
||||||
|
|
||||||
Что такое «REST»? В 2000 году один из авторов спецификаций HTTP и URI Рой Филдинг защитил докторскую диссертацию на тему «Архитектурные стили и дизайн архитектуры сетевого программного обеспечения», пятая глава которой была озаглавлена как «[Representational State Transfer (REST)](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)».
|
Что такое «REST»? В 2000 году один из авторов спецификаций HTTP и URI Рой Филдинг защитил докторскую диссертацию на тему «Архитектурные стили и дизайн архитектуры сетевого программного обеспечения», пятая глава которой была озаглавлена как «[Representational State Transfer (REST)](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)».
|
||||||
|
|
@@ -1,6 +1,6 @@
|
|||||||
### [Составляющие HTTP запросов и их семантика][http-api-requests-semantics]
|
### [Составляющие HTTP запросов и их семантика][http-api-requests-semantics]
|
||||||
|
|
||||||
Третье важное подготовительное упражнение, которое мы должны сделать — это дать описание формата HTTP-запросов и ответов и прояснить базовые понятия. Многое из написанного ниже может показаться читателю самоочевидным, но, увы, специфика протокола такова, что даже базовые сведения о нём, без которых мы не сможем двигаться дальше, разбросаны по обширной и фрагментированной документации, и даже опытные разработчики могут не знать тех или иных нюансов. Ниже мы попытаемся дать структурированный обзор протокола в том объёме, который необходим нам для проектирования HTTP API.
|
Важное подготовительное упражнение, которое мы должны сделать — это дать описание формата HTTP-запросов и ответов и прояснить базовые понятия. Многое из написанного ниже может показаться читателю самоочевидным, но, увы, специфика протокола такова, что даже базовые сведения о нём, без которых мы не сможем двигаться дальше, разбросаны по обширной и фрагментированной документации, и даже опытные разработчики могут не знать тех или иных нюансов. Ниже мы попытаемся дать структурированный обзор протокола в том объёме, который необходим нам для проектирования HTTP API.
|
||||||
|
|
||||||
В описании семантики и формата протокола мы будем руководствоваться свежевышедшим [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html), который заменил аж девять предыдущих спецификаций, описывавших разные аспекты технологии (при этом большое количество различной дополнительной функциональности всё ещё покрывается отдельными стандартами. В частности, принципы HTTP-кэширования описаны в отдельном [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111.html), а широко используемый в API метод `PATCH` так и не вошёл в основной RFC и регулируется [RFC 5789](https://www.rfc-editor.org/rfc/rfc5789.html)).
|
В описании семантики и формата протокола мы будем руководствоваться свежевышедшим [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html), который заменил аж девять предыдущих спецификаций, описывавших разные аспекты технологии (при этом большое количество различной дополнительной функциональности всё ещё покрывается отдельными стандартами. В частности, принципы HTTP-кэширования описаны в отдельном [RFC 9111](https://www.rfc-editor.org/rfc/rfc9111.html), а широко используемый в API метод `PATCH` так и не вошёл в основной RFC и регулируется [RFC 5789](https://www.rfc-editor.org/rfc/rfc5789.html)).
|
||||||
|
|
@@ -27,7 +27,7 @@
|
|||||||
* не размещайте модифицирующие операции за методом `GET` и неидемпотентные операции за `PUT` / `DELETE`;
|
* не размещайте модифицирующие операции за методом `GET` и неидемпотентные операции за `PUT` / `DELETE`;
|
||||||
* соблюдайте симметрию `GET` / `PUT` / `DELETE` методов;
|
* соблюдайте симметрию `GET` / `PUT` / `DELETE` методов;
|
||||||
* не позволяйте `GET` / `HEAD` / `DELETE`-запросам иметь тело, не возвращайте тело в ответе метода `HEAD` или совместно со статус-кодом `204 No Content`;
|
* не позволяйте `GET` / `HEAD` / `DELETE`-запросам иметь тело, не возвращайте тело в ответе метода `HEAD` или совместно со статус-кодом `204 No Content`;
|
||||||
* не придумывайте свой стандарт для передачи массивов и вложенных объектов в query — лучше воспользоваться HTTP-глаголом, позволяющим запросу иметь тело, или, в крайнем случае, передать параметры в виде base64-кодированного JSON-поля;
|
* не придумывайте свой стандарт для передачи массивов и вложенных объектов в query — лучше воспользоваться HTTP-глаголом, позволяющим запросу иметь тело, или, в крайнем случае, передать параметры в виде Base64-кодированного JSON-поля;
|
||||||
* не размещайте в пути и домене URL параметры, по формату требующие эскейпинга (т.е. могущие содержать символы, отличные от цифр и букв латинского алфавита); для этой цели лучше воспользоваться query-параметрами или телом запроса.
|
* не размещайте в пути и домене URL параметры, по формату требующие эскейпинга (т.е. могущие содержать символы, отличные от цифр и букв латинского алфавита); для этой цели лучше воспользоваться query-параметрами или телом запроса.
|
||||||
|
|
||||||
8. Ознакомьтесь хотя бы с основными видами уязвимостей в типичных имплементациях HTTP API, которыми могут воспользоваться злоумышленники:
|
8. Ознакомьтесь хотя бы с основными видами уязвимостей в типичных имплементациях HTTP API, которыми могут воспользоваться злоумышленники:
|
@@ -1,24 +0,0 @@
|
|||||||
### Заключительные положения и общие рекомендации
|
|
||||||
|
|
||||||
Подведём итог описанному в предыдущих главах. Чтобы разработать качественное HTTP API, необходимо:
|
|
||||||
1. Описать happy path, т.е. составить диаграмму вызовов для стандартного цикла работы клиентского приложения;
|
|
||||||
2. Понять, какие ошибки возможны на этом пути и каким образом клиент должен восстанавливаться из какого состояния;
|
|
||||||
3. Определиться с номенклатурой ресурсов и операций над ними;
|
|
||||||
4. Решить, какая функциональность будет передана на уровень протокола HTTP [какие стандартные возможности протокола будут использованы] и в каком объёме, и разработать конкретную спецификацию.
|
|
||||||
5. Детализировать пп. 1-3, т.е. записать все запросы и ответы согласно разработанной спецификации, и оценить, насколько удобным, понятным и читабельным оказалось результирующее API.
|
|
||||||
|
|
||||||
Позволим себе так же дать несколько советов по code style [которые в самоучителях принято выдавать за best practice разработки HTTP API]:
|
|
||||||
|
|
||||||
1. Не различайте пути с `/` на конце и без него и примите какую-то рекомендацию по умолчанию (мы рекомендуем все пути заканчивать на `/` — по простой причине, это позволяет разумно описать обращение к корню домена как `GET /`).
|
|
||||||
|
|
||||||
2. Не пренебрегайте стандартными заголовками — `Date`, `Content-Type`, `Content-Encoding`, `Content-Length`, `Cache-Control`, `Retry-After` — и вообще старайтесь не полагать на то, что клиент правильно догадывается о параметрах по умолчанию.
|
|
||||||
|
|
||||||
3. Поддержите метод `OPTIONS` и [протокол CORS](https://fetch.spec.whatwg.org/#cors-protocol) на случай, если ваш API захотят использовать из браузеров.
|
|
||||||
|
|
||||||
4. Определитесь с правилами выбора кейсинга параметров (и преобразований кейсинга при перемещении параметра между различными частями запроса) и придерживайтесь их.
|
|
||||||
|
|
||||||
5. Для всех `GET`-запросов указывайте политику кэширования (иначе всегда есть шанс, что клиент придумает её за вас).
|
|
||||||
|
|
||||||
6. Всегда оставляйте себе возможность обратно-совместимого расширения API и, в частности, всегда возвращайте JSON-объект в ответах эндпойтов — потому что приписать новые поля к объекту вы можете, а к массивам и примитивам — нет.
|
|
||||||
|
|
||||||
В заключение хотелось бы сказать следующее: HTTP API — это способ организовать ваше API так, чтобы полагаться на понимание семантики операций как разнообразным программным обеспечением, от клиентских фреймворков до серверных гейтвеев, так и разработчиком, который читает спецификацию. В этом смысле HTTP предоставляет пожалуй что наиболее широкий (и в плане глубины, и в плане распространённости) по сравнению с другими технологиями словарь для описания самых разнообразных ситуаций, возникающих во время работы клиент-серверных приложений. Разумеется, эта технология не лишена своих недостатков, но для разработчика публичного API она является выбором по умолчанию — на сегодняшний день скорее надо обосновывать отказ от HTTP API чем выбор в его пользу. Для непубличных API (особенно при наличии самописных инструментов кодогенерации) этот выбор не столь очевиден, но и здесь HTTP API обладает рядом преимуществ — большое количество готовых инструментов и широкое распространение знаний о технологии (пусти и фрагментарных) среди разработчиков.
|
|
@@ -3,7 +3,7 @@
|
|||||||
"author": "Сергей Константинов",
|
"author": "Сергей Константинов",
|
||||||
"chapter": "Глава",
|
"chapter": "Глава",
|
||||||
"toc": "Содержание",
|
"toc": "Содержание",
|
||||||
"description": "Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. Книга состоит из шести разделов, посвящённых проектированию API, паттернам дизайна API, поддержанию обратной совместимости, HTTP API и REST, SDK и UI-библиотекам, продуктовому управлению API.",
|
"description": "Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. Книга состоит из шести разделов, посвящённых проектированию API, паттернам дизайна API, поддержанию обратной совместимости, HTTP API и архитектурным принципам REST, SDK и UI-библиотекам, продуктовому управлению API.",
|
||||||
"publisher": "Сергей Константинов",
|
"publisher": "Сергей Константинов",
|
||||||
"copyright": "© Сергей Константинов, 2023",
|
"copyright": "© Сергей Константинов, 2023",
|
||||||
"locale": "ru_RU",
|
"locale": "ru_RU",
|
||||||
@@ -68,7 +68,8 @@
|
|||||||
"habrHref": "https://habr.com/ru/users/forgotten/",
|
"habrHref": "https://habr.com/ru/users/forgotten/",
|
||||||
"habrTag": "Хабре",
|
"habrTag": "Хабре",
|
||||||
"substackHref": "https://twirl.substack.com/",
|
"substackHref": "https://twirl.substack.com/",
|
||||||
"substackTag": "Substack"
|
"substackTag": "Substack",
|
||||||
|
"substackString": "twirl.substack.com"
|
||||||
},
|
},
|
||||||
"sourceCodeAt": "Исходный код доступен на",
|
"sourceCodeAt": "Исходный код доступен на",
|
||||||
"frontPage": {
|
"frontPage": {
|
||||||
@@ -80,7 +81,7 @@
|
|||||||
"<ul><li>проектированию API,</li>",
|
"<ul><li>проектированию API,</li>",
|
||||||
"<li>паттернам дизайна API,</li>",
|
"<li>паттернам дизайна API,</li>",
|
||||||
"<li>поддержанию обратной совместимости,</li>",
|
"<li>поддержанию обратной совместимости,</li>",
|
||||||
"<li>HTTP API и REST,</li>",
|
"<li>HTTP API и архитектурным принципам REST,</li>",
|
||||||
"<li>SDK и UI-библиотекам,</li>",
|
"<li>SDK и UI-библиотекам,</li>",
|
||||||
"<li>продуктовому управлению API.</li></ul>",
|
"<li>продуктовому управлению API.</li></ul>",
|
||||||
"<p class=\"text-align-left\">Иллюстрации и вдохновение: Maria Konstantinova · <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>",
|
"<p class=\"text-align-left\">Иллюстрации и вдохновение: Maria Konstantinova · <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>",
|
||||||
@@ -102,7 +103,7 @@
|
|||||||
"<ul><li>— проектированию API,</li>",
|
"<ul><li>— проектированию API,</li>",
|
||||||
"<li>— паттернам дизайна API,</li>",
|
"<li>— паттернам дизайна API,</li>",
|
||||||
"<li>— поддержанию обратной совместимости,</li>",
|
"<li>— поддержанию обратной совместимости,</li>",
|
||||||
"<li>— HTTP API и REST,</li>",
|
"<li>— HTTP API и архитектурным принципам REST,</li>",
|
||||||
"<li>— SDK и UI-библиотекам,</li>",
|
"<li>— SDK и UI-библиотекам,</li>",
|
||||||
"<li>— продуктовому управлению API.</li></ul>",
|
"<li>— продуктовому управлению API.</li></ul>",
|
||||||
"<p>Иллюстрации и вдохновение: Maria Konstantinova · <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>"
|
"<p>Иллюстрации и вдохновение: Maria Konstantinova · <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>"
|
||||||
|
@@ -76,8 +76,8 @@ module.exports = {
|
|||||||
l10n.links.emailString
|
l10n.links.emailString
|
||||||
}</a> · <a target="_blank" href="${l10n.links.linkedinHref}">${
|
}</a> · <a target="_blank" href="${l10n.links.linkedinHref}">${
|
||||||
l10n.links.linkedinString
|
l10n.links.linkedinString
|
||||||
}</a> · <a target="_blank" href="${l10n.links.patreonHref}">${
|
}</a> · <a target="_blank" href="${l10n.links.substackHref}">${
|
||||||
l10n.links.patreonString
|
l10n.links.substackString
|
||||||
}</a></p>
|
}</a></p>
|
||||||
${l10n.frontPage.contents.join('\n')}
|
${l10n.frontPage.contents.join('\n')}
|
||||||
<p class=\"text-align-left\">${
|
<p class=\"text-align-left\">${
|
||||||
|
Reference in New Issue
Block a user