1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-08-10 21:51:42 +02:00
This commit is contained in:
Sergey Konstantinov
2023-05-22 20:58:51 +03:00
parent 5136925f79
commit 79b0095ca6
6 changed files with 32 additions and 32 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -586,7 +586,7 @@ ul.references li p a.back-anchor {
</div><div class="page-break"></div><div class="annotation"><p class="text-align-left">
<strong>Сергей Константинов. API.</strong><br>
<a target="_blank" href="mailto:yatwirl@gmail.com">yatwirl@gmail.com</a> · <a target="_blank" href="https://www.linkedin.com/in/twirl/">linkedin.com/in/twirl</a> · <a target="_blank" href="https://www.patreon.com/yatwirl">patreon.com/yatwirl</a></p>
<p>«API-first» подход — одна из самых горячих горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>
<p>«API-first» подход — одна из самых горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>
<p>Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. Книга состоит из шести разделов, посвящённых:</p>
<ul><li>проектированию API,</li>
<li>паттернам дизайна API,</li>
@@ -638,7 +638,7 @@ ul.references li p a.back-anchor {
<li>либо клиент-серверное взаимодействие опирается на описанные в стандарте HTTP возможности (точнее было бы сказать, «стандартах HTTP», поскольку функциональность протокола описана во множестве разных RFC);</li>
<li>либо HTTP утилизируется как транспорт, и поверх него выстроен дополнительный уровень абстракции (т.е. возможности HTTP, такие как номенклатура ошибок или заголовков, сознательно редуцируются до минимального уровня, а вся мета-информация переносится на уровень вышестоящего протокола).</li>
</ul>
<p>К первой категории относятся API, которые принято обозначать словом «REST» или «RESTful». а ко второй — все виды RPC, а также прикладные протоколы типа SSH.</p>
<p>К первой категории относятся API, которые принято обозначать словом «REST» или «RESTful», а ко второй — все виды RPC, а также прикладные протоколы типа SSH.</p>
<p><strong>Во-вторых</strong>, реализации HTTP API опираются на разные форматы передаваемых данных:</p>
<ul>
<li>REST API и некоторые RPC (<a href="https://www.jsonrpc.org/">JSON-RPC</a>, <a href="https://graphql.org/">GraphQL</a>) полагаются в основном на формат <a href="https://www.ecma-international.org/publications-and-standards/standards/ecma-404/">JSON</a> (опционально дополненный передачей бинарных файлов);</li>
@@ -1770,7 +1770,7 @@ PUT /v1/users/{id}
<p><strong>Хорошо</strong>: <code>getOrders({ limit, parameters })</code> — должно существовать ограничение сверху на размер обрабатываемых и возвращаемых данных и, соответственно, возможность уточнить запрос, если партнёру всё-таки требуется большее количество данных, чем разрешено обрабатывать в одном запросе.</p>
<h5><a href="#chapter-13-paragraph-11" id="chapter-13-paragraph-11" class="anchor">11. Описывайте политику перезапросов</a></h5>
<p>Одна из самых больших проблем с точки зрения производительности, с которой сталкивается почти любой разработчик API, и внутренних, и публичных — это отказ в обслуживании вследствие лавины перезапросов: временные проблемы на бэкенде API (например, повышение времени ответа) могут привести к полной неработоспособности сервера, если клиенты начнут очень быстро повторять запрос, не получив или не дождавшись ответа, сгенерировав, таким образом, кратно большую нагрузку в короткий срок.</p>
<p>Лучшая практика в такой ситуации — это требовать, чтобы клиенты перезапрашивали эндпойнты API с увеличивающимся интервалом (скажем, перевый перезапрос происходит через одну секунду, второй — через две, третий через четыре, и так далее, но не больше одной минуты). Конечно, в случае публичного API такое требование никто не обязан соблюдать, но и хуже от его наличия вам точно не станет: хотя бы часть партнёров прочитает документацию и последует вашим рекомендациям.</p>
<p>Лучшая практика в такой ситуации — это требовать, чтобы клиенты перезапрашивали эндпойнты API с увеличивающимся интервалом (скажем, первый перезапрос происходит через одну секунду, второй — через две, третий через четыре, и так далее, но не больше одной минуты). Конечно, в случае публичного API такое требование никто не обязан соблюдать, но и хуже от его наличия вам точно не станет: хотя бы часть партнёров прочитает документацию и последует вашим рекомендациям.</p>
<p>Кроме того, вы можете разработать референсную реализацию политики перезапросов в ваших публичных SDK и следить за правильностью имплементации open-source модулей к вашему API.</p>
<h5><a href="#chapter-13-paragraph-12" id="chapter-13-paragraph-12" class="anchor">12. Считайте трафик</a></h5>
<p>В современном мире такой ресурс, как объём переданного трафика, считать уже почти не принято — считается, что Интернет всюду практически безлимитен. Однако он всё-таки не абсолютно безлимитен: всегда можно спроектировать систему так, что объём трафика окажется некомфортным даже и для современных сетей.</p>
@@ -2472,7 +2472,7 @@ GET /v1/runtimes/{runtime_id}/state
<pre><code>// Прекращает исполнение рантайма
POST /v1/runtimes/{id}/terminate
</code></pre><div class="page-break"></div><h2><a href="#section-3" class="anchor" id="section-3">[В разработке] Раздел II. Паттерны дизайна API</a></h2><h3><a href="#chapter-15" class="anchor" id="chapter-15">Глава 15. О паттернах проектирования в контексте API</a></h3>
<p>Концепция <a href="https://en.wikipedia.org/wiki/Software_design_pattern#History">«паттернов»</a> в области разработки программного обеспечения была введёна Кентом Бэком и Уордом Каннингемом в 1987 году, и популяризирован «бандой четырёх» (Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес) в их книге «Приёмы объектно-ориентированного проектирования. Паттерны проектирования», изданной в 1994 году. Согласно общепринятому определению, паттерны программирования — «повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста».</p>
<p>Концепция <a href="https://en.wikipedia.org/wiki/Software_design_pattern#History">«паттернов»</a> в области разработки программного обеспечения была введена Кентом Бэком и Уордом Каннингемом в 1987 году, и популяризирован «бандой четырёх» (Эрих Гамма, Ричард Хелм, Ральф Джонсон и Джон Влиссидес) в их книге «Приёмы объектно-ориентированного проектирования. Паттерны проектирования», изданной в 1994 году. Согласно общепринятому определению, паттерны программирования — «повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста».</p>
<p>Если мы говорим об API, особенно если конечным потребителем этих API является разработчик (интерфейсы фреймворков, операционных систем), классические паттерны проектирования вполне к ним применимы. И действительно, многие из описанных в предыдущем разделе примеров представляют собой применение того или иного паттерна.</p>
<p>Однако, если мы попытаемся обобщить этот подход на разработку API в целом, то увидим, что большинство типичных проблем дизайна API являются более высокоуровневыми и не сводятся к базовым паттернам разработки ПО. Скажем, проблемы кэширования ресурсов (и инвалидации кэша) или организация пагинации классиками не покрыты.</p>
<p>В рамках этого раздела мы попытаемся описать те задачи проектирования API, которые представляются нам наиболее важными. Мы не претендуем здесь на то, чтобы охватить <em>все</em> проблемы и тем более — все решения, и скорее фокусируемся на описании подходов к решению типовых задач с их достоинствами и недостатками. Мы понимаем, что читатель, знакомый с классическими трудами «банды четырёх», Гради Буча и Мартина Фаулера ожидает от раздела с названием «Паттерны API» большей системности и ширины охвата, и заранее просим у него прощения.</p>
@@ -2729,7 +2729,7 @@ const pendingOrders = await api.
<li>нативное же обеспечение устойчивости к временному всплеску нагрузки на сервис — новые задачи встают в очередь (возможно, приоритизированную), фактически имплементируя <a href="https://en.wikipedia.org/wiki/Token_bucket">«маркерное ведро»</a>;</li>
<li>организация взаимодействия в тех случаях, когда время исполнения операции превышает разумные значения (в случае сетевых API — типичное время срабатывания сетевых таймаутов, т.е. десятки секунд) либо является непредсказуемым.</li>
</ul>
<p>Кроме того, асихнронное взаимодействие удобнее с точки зрения развития API в будущем: устройство системы, обрабатывающей такие запросы, может меняться в сторону усложнения и удлинения конвейера исполнения задачи, в то время как синхронным функциям придётся укладываться в разумные временные рамки, чтобы оставаться синхронными — что, конечно, ограничивает возможности рефакторинга внутренних механик.</p>
<p>Кроме того, асинхронное взаимодействие удобнее с точки зрения развития API в будущем: устройство системы, обрабатывающей такие запросы, может меняться в сторону усложнения и удлинения конвейера исполнения задачи, в то время как синхронным функциям придётся укладываться в разумные временные рамки, чтобы оставаться синхронными — что, конечно, ограничивает возможности рефакторинга внутренних механик.</p>
<p><strong>NB</strong>: иногда можно встретить решение, при котором эндпойнт имеет двойной интерфейс и может вернуть как результат, так и ссылку на исполнение задания. Хотя для вас как разработчика API он может выглядеть логично (смогли «быстро» выполнить запрос, например, получить результат из кэша — вернули ответ; не смогли — вернули ссылку на задание), для пользователей API это решение крайне неудобно, поскольку заставляет поддерживать две ветки кода одновременно. Также встречается парадигма предоставления на выбор разработчику два набора эндпойнтов, синхронный и асинхронный, но по факту это просто перекладывание ответственности на партнёра.</p>
<p>Популярность данного паттерна также обусловлена тем, что многие современные микросервисные архитектуры «под капотом» также взаимодействуют асинхронно — либо через потоки событий, либо через промежуточный pub/sub сервис, реализующий паттерн «издатель-подписчик». Имплементация аналогичной асинхронности во внешнем API является самым простым способом обойти возникающие проблемы (читай, те же непредсказуемые и возможно очень большие задержки выполнения операций). Доходит до того, что в некоторых API абсолютно все операции делаются асинхронными (включая чтение данных), даже если никакой необходимости в этом нет.</p>
<p>Мы, однако, не можем не отметить, что, несмотря на свою привлекательность, повсеместная асинхронность влечёт за собой ряд достаточно неприятных проблем.</p>
@@ -3260,7 +3260,7 @@ GET /v1/orders/pending
<li>или обработка событий требует последовательного исполнения и не подразумевает параллельности.</li>
</ul>
<p><strong>NB</strong>: третий (и отчасти второй) варианты естественным образом приводят нас к схеме, характерной для клиентских устройств: push-уведомление само по себе не почти содержит полезной информации и только является сигналом для внеочередного поллинга.</p>
<p>Применение техник с отправкой только ограниченного набора данных помимо усложнения схемы взаимодействия и увеличения количества запросов имеет ещё один важный недостаток. Если в варианте 1 (сообщение содержит в себе все релевантные данные) мы можем рассчитывать на то, что возврат кода успеха подписчиком эквивалентен успешной обработке сообщения партнёром (что, вообще говоря, тоже не гарантировано, т.к. партнёр может использовать асинхронные схемы), то для вариантов 2 и 3 это заведомо не так: для обработки сообщений партнёр должен выполнить дополнительные действия, начиная с получения нужных данных о заказе. В этом случае нам необходимо иметь раздельные статусы — сообщение доставлено и сообщение обработано; в идеале, второе должно вытекать из логики работы API, т.е. сигналом о том, что сообщение обработано, является какое-то действие, совершаемое партнёром. В нашем кофейном примере это может быть перевод заказа партнёром из статуса <code>"new"</code> (заказ создан пользователем) в статус <code>"accepted"</code> или <code>"rejected"</code> (кофейня партнёра приняла или отклонила заказ). Тогда полный цикл обработки уведосления будет выглядеть так:</p>
<p>Применение техник с отправкой только ограниченного набора данных помимо усложнения схемы взаимодействия и увеличения количества запросов имеет ещё один важный недостаток. Если в варианте 1 (сообщение содержит в себе все релевантные данные) мы можем рассчитывать на то, что возврат кода успеха подписчиком эквивалентен успешной обработке сообщения партнёром (что, вообще говоря, тоже не гарантировано, т.к. партнёр может использовать асинхронные схемы), то для вариантов 2 и 3 это заведомо не так: для обработки сообщений партнёр должен выполнить дополнительные действия, начиная с получения нужных данных о заказе. В этом случае нам необходимо иметь раздельные статусы — сообщение доставлено и сообщение обработано; в идеале, второе должно вытекать из логики работы API, т.е. сигналом о том, что сообщение обработано, является какое-то действие, совершаемое партнёром. В нашем кофейном примере это может быть перевод заказа партнёром из статуса <code>"new"</code> (заказ создан пользователем) в статус <code>"accepted"</code> или <code>"rejected"</code> (кофейня партнёра приняла или отклонила заказ). Тогда полный цикл обработки уведомления будет выглядеть так:</p>
<pre><code>// Уведомляем партнёра о том,
// что его реакции
// ожидают три новых заказа
@@ -3502,7 +3502,7 @@ object.observe('widthchange', observerFunction);
</code></pre>
<p>Допустим, в какой-то момент вы решили надёжным клиентам с хорошей историей заказов предоставлять кофе «в кредит», не дожидаясь подтверждения платежа. Т.е. заказ перейдёт в статус <code>"preparing_started"</code>, а может и <code>"ready"</code>, вообще без события <code>"payment_approved"</code>. Вам может показаться, что это изменение является обратно-совместимым — в самом деле, вы же и не обещали никакого конкретного порядка событий. Но это, конечно, не так.</p>
<p>Предположим, что у разработчика (вероятно, бизнес-партнёра вашей компании) написан какой-то код, выполняющий какую-то полезную бизнес функцию поверх этих событий — например, строит аналитику по затратам и доходам. Вполне логично ожидать, что этот код будет оперировать какой-то машиной состояний, которая будет переходить в то или иное состояние в зависимости от получения или неполучения события. Аналитический код наверняка сломается вследствие изменения порядка событий. В лучшем случае разработчик увидит какие-то исключения и будет вынужден разбираться с причиной; в худшем случае партнёр будет оперировать неправильной статистикой неопределённое время, пока не найдёт в ней ошибку.</p>
<p>Правильным решением было бы во-первых, изначально задокументировать порядок событий и допустимые состояния; во-вторых, продолжать генерировать событие <code>"payment_approved"</code> перед <code>"preparing_started"</code> (если вы приняли решение исполнять такой заказ — значит, по сути, подтвердили платёж) и добавить расширенную информацию о платеже.</p>
<p>Правильным решением было бы, во-первых, изначально задокументировать порядок событий и допустимые состояния; во-вторых, продолжать генерировать событие <code>"payment_approved"</code> перед <code>"preparing_started"</code> (если вы приняли решение исполнять такой заказ — значит, по сути, подтвердили платёж) и добавить расширенную информацию о платеже.</p>
<p>Этот пример подводит нас к ещё к одному правилу.</p>
<h5><a href="#chapter-27-paragraph-4" id="chapter-27-paragraph-4" class="anchor">4. Продуктовая логика тоже должна быть обратно совместимой</a></h5>
<p>Такие критичные вещи, как граф переходов между статусами, порядок событий и возможные причины тех или иных изменений — должны быть документированы. Далеко не все детали бизнес-логики можно выразить в форме контрактов на эндпойнты, а некоторые вещи нельзя выразить вовсе.</p>
@@ -3668,7 +3668,7 @@ PUT /formatters/volume/ru/US
"template": "{volume} ун."
}
</code></pre>
<p>Таким образом, реалиазция вышеупомянутой функции <code>l10n.volume.format</code> сможет извлечь правила для локали <code>ru/US</code> и отформатировать представление объёма согласно им.</p>
<p>Таким образом, реализация вышеупомянутой функции <code>l10n.volume.format</code> сможет извлечь правила для локали <code>ru/US</code> и отформатировать представление объёма согласно им.</p>
<p><strong>NB</strong>: мы, разумеется, в курсе, что таким простым форматом локализации единиц измерения в реальной жизни обойтись невозможно, и необходимо либо положиться на существующие библиотеки, либо разработать сложный формат описания (учитывающий, например, падежи слов и необходимую точность округления), либо принимать правила форматирования в императивном виде (т.е. в виде кода функции). Пример выше приведён исключительно в учебных целях.</p>
<p>Вернёмся теперь к проблеме <code>name</code> и <code>description</code>. Для того, чтобы снизить связность в этом аспекте, нужно прежде всего формализовать (возможно, для нас самих, необязательно во внешнем API) понятие «макета». Мы требуем <code>name</code> и <code>description</code> не просто так в вакууме, а чтобы представить их во вполне конкретном UI. Этому конкретному UI можно дать идентификатор или значимое имя.</p>
<pre><code>GET /v1/layouts/{layout_id}
@@ -4115,7 +4115,7 @@ ProgramContext.dispatch = (action) => {
<li>внутренние потребители, как правило, используют вполне конкретный технический стек, и API плохо оптимизирован под любые другие языки программирования / операционные системы / фреймворки;</li>
<li>внутренние потребители гораздо лучше погружены в контекст, могут посмотреть исходный код или пообщаться напрямую с разработчиком API, и для них порог входа в технологию находится на совершенно другом уровне по сравнению с внешними потребителями;</li>
<li>документация покрывает какой-то наиболее востребованный внутренними потребителями срез сценариев;</li>
<li>линейка сервисов API, о которой мы расскажем ниже, зачастую попросту отсутствует, т.к. внутренними потребителям она не нужна.</li>
<li>линейка сервисов API, о которой мы расскажем ниже, зачастую попросту отсутствует, т.к. внутренним потребителям она не нужна.</li>
</ul>
</li>
<li>Любые ресурсы выделяются в первую очередь на поддержку внутренних потребителей. Это означает, что:
@@ -4140,7 +4140,7 @@ ProgramContext.dispatch = (action) => {
<h5><a href="#chapter-52-paragraph-7" id="chapter-52-paragraph-7" class="anchor">7. API = инструмент получения обратной связи и UGC</a></h5>
<p>Если компания располагает какими-то большими данными, то оправданной может быть стратегия выпуска публичного API для того, чтобы конечные пользователи вносили исправления в данные или иным образом вовлекались в их разметку. Например, провайдеры картографических API часто разрешают сообщить об ошибке или исправить неточность прямо в стороннем приложении. [А в случае нашего кофейного API мы могли бы собирать обратную связь, как пассивно — строить рейтинги заведений, например, — так и активно — контактировать с владельцами заведений чтобы помочь им исправить недостатки; находить через UGC ещё не подключенные к API кофейни и проактивно работать с ними.]</p>
<h5><a href="#chapter-52-paragraph-8" id="chapter-52-paragraph-8" class="anchor">8. Терраформирование</a></h5>
<p>Наконец, наиболее альтруистический подход к разработке API — предоставление его либо полностью бесплатно либо в формате открытого кода и открытых данных просто с целью изменения ландшафта: если сегодня за API никто не готов платить, то можно вложиться в популяризацию и продвижение функциональности, рассчитывая найти коммерческие ниши (в любом из перечисленных форматов) в будущем или повысить значимость и полезность API в глазах конечных пользователей (а значит и готовность потребителей платить за использование API). [В случае нашего кофейного примера — если компания-производитель умных кофе-машин предоставляет API полностью бесплатно в расчёте на то, что со временем наличие у кофе-машин API станет нормой, и появится возможность разработать новые монетизируемые сервисы за счёт этого.]</p>
<p>Наконец, наиболее альтруистический подход к разработке API — предоставление его либо полностью бесплатно, либо в формате открытого кода и открытых данных просто с целью изменения ландшафта: если сегодня за API никто не готов платить, то можно вложиться в популяризацию и продвижение функциональности, рассчитывая найти коммерческие ниши (в любом из перечисленных форматов) в будущем или повысить значимость и полезность API в глазах конечных пользователей (а значит и готовность потребителей платить за использование API). [В случае нашего кофейного примера — если компания-производитель умных кофе-машин предоставляет API полностью бесплатно в расчёте на то, что со временем наличие у кофе-машин API станет нормой, и появится возможность разработать новые монетизируемые сервисы за счёт этого.]</p>
<h5><a href="#chapter-52-paragraph-9" id="chapter-52-paragraph-9" class="anchor">9. Серая зона</a></h5>
<p>Отдельный источник дохода для разработчика API — это анализ запросов, которые делают конечные пользователи или, иными словами, сбор и дальнейшая продажа какой-то информации. Следует иметь в виду, что граница между допустимыми способами обработки информации (например, агрегирование поисковых запросов с целью выделения трендов или потенциально прибыльных точек для открытия кофейни) и недопустимыми здесь весьма неочевидна и имеет тенденцию меняться как во времени, так и в пространстве (в смысле, одни и те же действия могут быть легальны по одну сторону государственной границы и нелегальны по другую), так что принимать решения о монетизации API подобными способами следует с очень большой осторожностью.</p>
<h4>Подход API-first</h4>
@@ -4192,7 +4192,7 @@ ProgramContext.dispatch = (action) => {
<p>в частности, разработчики скептически относятся к громким рекламным заявлениям и готовы разбираться в том, насколько эти заявления соответствуют истине;</p>
</li>
<li>
<p>к разработчикам крайне сложно обращаться через стандартные маркетинговые каналы; помимо того, что они получают информацию в основном из ускоспециализированных сообществ, программисты также смотрят в первую очередь на мнения, подтверждённые конкретными цифрами и примерами (желательно — примерами кода);</p>
<p>к разработчикам крайне сложно обращаться через стандартные маркетинговые каналы; помимо того, что они получают информацию в основном из узкоспециализированных сообществ, программисты также смотрят в первую очередь на мнения, подтверждённые конкретными цифрами и примерами (желательно — примерами кода);</p>
<ul>
<li>мнения «инфлюэнсеров» в такой среде значат очень мало, поскольку ничьё мнение не принимается на веру;</li>
</ul>
@@ -4362,7 +4362,7 @@ ProgramContext.dispatch = (action) => {
<p>Проблема же API-ключей заключается в том, что они <em>не позволяют</em> надёжно идентифицировать ни приложение, ни владельца.</p>
<p>Если API предоставляется с какими-то бесплатными лимитами, то велик соблазн завести множество ключей, оформленных на разных владельцев, чтобы оставаться в рамках бесплатных лимитов. Вы можете повышать стоимость заведения таких мультиаккаунтов, например, требуя привязки номера телефона или кредитной карты, однако и то, и другое — в настоящий момент широко распространённая услуга. Выпуск виртуальных телефонных номеров или виртуальных кредитных карт (не говоря уже о нелегальных способах приобрести краденые) всегда будет дешевле, чем честная оплата использования API — если, конечно, это не API выпуска карт или номеров. Таким образом, идентификация пользователя по ключам (если только ваш API не является чистым B2B и для его использования нужно подписать физический договор) никак не освобождает от необходимости перепроверять, действительно ли пользователь соблюдает правила и не заводит множество ключей для одного приложения.</p>
<p>Другая опасность заключается в том, что ключ могут банально украсть у добросовестного партнёра; в случае клиентских и веб-приложений это довольно тривиально.</p>
<p>Может показаться, что в случае предоставления серверных API проблема воровства ключей неактуальна, но, на самом деле, это не так. Предположим, что партнёр предоставляет свой собственный публичный сервис, который «под капотом» использует ваше API. Это часто означает, что в сервисе партнёра есть эндпойнт, предназначенный для конечных пользователей, который внутри делает запрос к API и возвращает результат, и этот эндпойнт может использоваться злоумышленником как эквивалент API. Конечно, можно объявить такой фрод проблемой партнёра, однако было бы, во-первых, наивно ожидать от каждого партнёра реализации собственной антифрод-системы, которая позволит выявлять таких недобросовестных пользователей, и, во-вторых, это попросту неэффективно: очевидно, что централизованная система борьбы с фродерами всегда будет более эффективной, нежели множество частных любительских реализаций. К томе же, и серверные ключи могут быть украдены: это сложее, чем украсть клиентские, но не невозможно. Популярный API рано или поздно столкнётся с тем, что украденные ключи будут выложены в свободный доступ (или владелец ключа просто будет делиться им со знакомыми по доброте душевной).</p>
<p>Может показаться, что в случае предоставления серверных API проблема воровства ключей неактуальна, но, на самом деле, это не так. Предположим, что партнёр предоставляет свой собственный публичный сервис, который «под капотом» использует ваше API. Это часто означает, что в сервисе партнёра есть эндпойнт, предназначенный для конечных пользователей, который внутри делает запрос к API и возвращает результат, и этот эндпойнт может использоваться злоумышленником как эквивалент API. Конечно, можно объявить такой фрод проблемой партнёра, однако было бы, во-первых, наивно ожидать от каждого партнёра реализации собственной антифрод-системы, которая позволит выявлять таких недобросовестных пользователей, и, во-вторых, это попросту неэффективно: очевидно, что централизованная система борьбы с фродерами всегда будет более эффективной, нежели множество частных любительских реализаций. К томе же, и серверные ключи могут быть украдены: это сложнее, чем украсть клиентские, но не невозможно. Популярный API рано или поздно столкнётся с тем, что украденные ключи будут выложены в свободный доступ (или владелец ключа просто будет делиться им со знакомыми по доброте душевной).</p>
<p>Так или иначе, встаёт вопрос независимой валидации: каким образом можно проконтролировать, действительно ли API используется потребителем в соответствии с пользовательским соглашением.</p>
<p>Мобильные приложения удобно отслеживаются по идентификатору приложения в соответствующем сторе (Google Play, App Store и другие), поэтому разумно требовать от партнёров идентифицировать приложение при подключении API. Вебсайты с некоторой точностью можно идентифицировать по заголовкам <code>Referer</code> или <code>Origin</code> (и для надёжности можно так же потребовать от партнёра указывать домен сайта при инициализации API).</p>
<p>Эти данные сами по себе не являются надёжными; важно то, что они позволяют проводить кросс-проверки:</p>
@@ -4411,7 +4411,7 @@ ProgramContext.dispatch = (action) => {
<h5><a href="#chapter-59-paragraph-2" id="chapter-59-paragraph-2" class="anchor">2. Запрос дополнительного фактора аутентификации</a></h5>
<p>Поскольку и статический, и поведенческий анализ эвристические, очень желательно не просто выносить решение на их основе, но предлагать подозрительным пользователям дополнительно доказать, что они совершают легитимные запросы. Если такие механизмы есть, качество работы анти-фрод системы существенно возрастает, поскольку тогда допустимо будет снизить порог срабатывания или вовсе включить проактивную защиту, т.е. предлагать пользователям пройти дополнительную проверку превентивно.</p>
<p>В случае сервисов для конечных пользователей основным методом дополнительной аутентификации является перенаправление на страницу с капчей. В случае API это может быть весьма проблематично, особенно если вы пренебрегли советом <a href="#chapter-11-paragraph-19">«Предусмотрите ограничения»</a> — во многих случаях вам придётся переложить имплементацию этого сценария на партнёра (т.е. это партнёр должен будет показывать капчу и идентифицировать пользователя, основываясь на сигналах, поступающих от эндпойнтов API) что, конечно, сильно снижает комфортность работы с таким API.</p>
<p><strong>NB</strong>. Вместо капчи здесь могут быть любые другие действия, вводящие дополнительные факторы аутентификации. Это может быть, например, подтверждение номера телефона или второй шаг протокола 3D-Secure. Важно здесь то, что запрос второго шага аутентификации должен быть предусмотрен в API, поскольку добавить его его обратно совместимым образом к существующим endpoint-ам нельзя.</p>
<p><strong>NB</strong>. Вместо капчи здесь могут быть любые другие действия, вводящие дополнительные факторы аутентификации. Это может быть, например, подтверждение номера телефона или второй шаг протокола 3D-Secure. Важно здесь то, что запрос второго шага аутентификации должен быть предусмотрен в API, поскольку добавить его обратно совместимым образом к существующим endpoint-ам нельзя.</p>
<p>Другие популярные способы распознать робота — предложить ему приманку (honeypot) или использовать методы проверки среды исполнения (начиная от достаточно простых вроде исполнения JavaScript на странице и заканчивая технологиями проверки целостности приложения).</p>
<h5><a href="#chapter-59-paragraph-3" id="chapter-59-paragraph-3" class="anchor">3. Ограничение доступа</a></h5>
<p>Видимость богатства способов технической идентификации пользователей, увы, разбивается о суровую реальность наличия у вас очень скромных средств ограничения доступа. Бан по cookie / <code>Referer</code>-у / <code>User-Agent</code>-у практически не работает по той причине, что эти данные передаёт клиент, и он же легко может их подменить. По большому счёту, способов ограничения доступа у вас четыре:</p>
@@ -4495,7 +4495,7 @@ ProgramContext.dispatch = (action) => {
<p>Обратный сценарий — когда техподдержка предоставляется только на платной основе, и на вопросы отвечают непосредственно разработчики; пусть на качество и релевантность запросов такая модель не оказывает большого влияния (вашим API продолжают пользоваться, в основном, новички; вы лишь отсекаете тех из них, у кого нет денег на платную поддержку), но, по крайней мере, вы не будете испытывать проблем с наймом, поскольку сможете позволить себе роскошь поставить технического специалиста на первую линию поддержки.</p>
</li>
<li>
<p>Частично или полностью проблему с поддержкой новичков может снять развитое комьюнити (см. главу <a href="">«Взаимодействие с разработчиками»</a>). Как правило, члены комьюнити в состоянии ответить на вопросы новичков, особенно если им активно помогают модераторы.</p>
<p>Частично или полностью проблему с поддержкой новичков может снять развитое комьюнити (см. главу <a href="">«Взаимодействие с разработчиками»</a>). Как правило, члены комьюнити в состоянии ответить на вопросы новичков, особенно если им активно помогают модераторы.</p>
</li>
</ol>
<p>Важный момент заключается в том, что, какой вариант оказания техподдержки вы ни выберете, финально на вопросы пользователей придётся отвечать разработчикам API просто в силу того факта, что полноценно разобраться в коде партнёра может только программист. Из этого следует два важных вывода.</p>

Binary file not shown.

View File

@@ -42,7 +42,7 @@
<br />Поддержите эту работу на <a class="patreon" href="https://www.patreon.com/yatwirl">Patreon</a>
<br />Поделиться: <a class="share share-facebook" href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html" target="_blank"></a> · <a class="share share-twitter" href="https://twitter.com/intent/tweet?text=%C2%ABAPI%C2%BB%20%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D1%8F%20%D0%9A%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82%D0%B8%D0%BD%D0%BE%D0%B2%D0%B0%20%E2%80%94%20%D0%BA%D0%BD%D0%B8%D0%B3%D0%B0%20%D0%BE%20%D0%B4%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD%D0%B5%20API%20%D0%B8%20%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D0%BE%D0%B2%D0%BE%D0%BC%20%D0%B8%20%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC%20%D1%80%D0%B0%D0%B7%D0%B2%D0%B8%D1%82%D0%B8%D0%B8&url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html&hashtags=API%2CTheAPIBook&via=blogovodoved" target="_blank"></a> · <a class="share share-linkedin" href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html" target="_blank"></a> · <a class="share share-reddit" href="http://www.reddit.com/submit?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html&title=%C2%ABAPI%C2%BB%20%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D1%8F%20%D0%9A%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82%D0%B8%D0%BD%D0%BE%D0%B2%D0%B0%20%E2%80%94%20%D0%BA%D0%BD%D0%B8%D0%B3%D0%B0%20%D0%BE%20%D0%B4%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD%D0%B5%20API%20%D0%B8%20%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D0%BE%D0%B2%D0%BE%D0%BC%20%D0%B8%20%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC%20%D1%80%D0%B0%D0%B7%D0%B2%D0%B8%D1%82%D0%B8%D0%B8" target="_blank"></a><br/>⚙️⚙️⚙️
</nav>
<p>«API-first» подход — одна из самых горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>
<p>«API-first» подход — одна из самых горячих горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>
<p>Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. Книга состоит из шести разделов, посвящённых:</p>
<ul><li>— проектированию API,</li>
<li>— паттернам дизайна API,</li>
@@ -57,9 +57,9 @@
<ul><li>
<h4><a href="API.ru.html#section-1">Введение</a></h4>
<ul>
<li><a href="API.ru.html#intro-structure">Глава 1. О структуре этой книги</a></li>
<li><a href="API.ru.html#intro-structure">Глава 1. О структуре этой книги</a></li>
<li><a href="API.ru.html#intro-api-definition">Глава 2. Определение API</a></li>
<li><a href="API.ru.html#intro-api-solutions-overview">Глава 3. Обзор существующих решений в области разработки API</a></li>
<li><a href="API.ru.html#intro-api-solutions-overview">Глава 3. Обзор существующих решений в области разработки API</a></li>
<li><a href="API.ru.html#intro-api-quality">Глава 4. Критерии качества API</a></li>
<li><a href="API.ru.html#intro-api-first-approach">Глава 5. API-first подход</a></li>
<li><a href="API.ru.html#intro-back-compat">Глава 6. Обратная совместимость</a></li>
@@ -72,10 +72,10 @@
<ul>
<li><a href="API.ru.html#api-design-context-pyramid">Глава 9. Пирамида контекстов API</a></li>
<li><a href="API.ru.html#api-design-defining-field">Глава 10. Определение области применения</a></li>
<li><a href="API.ru.html#api-design-separating-abstractions">Глава 11. Разделение уровней абстракции</a></li>
<li><a href="API.ru.html#api-design-isolating-responsibility">Глава 12. Разграничение областей ответственности</a></li>
<li><a href="API.ru.html#api-design-describing-interfaces">Глава 13. Описание конечных интерфейсов</a></li>
<li><a href="API.ru.html#api-design-annex">Глава 14. Приложение к разделу I. Модельный API</a></li>
<li><a href="API.ru.html#api-design-separating-abstractions">Глава 11. Разделение уровней абстракции</a></li>
<li><a href="API.ru.html#api-design-isolating-responsibility">Глава 12. Разграничение областей ответственности</a></li>
<li><a href="API.ru.html#api-design-describing-interfaces">Глава 13. Описание конечных интерфейсов</a></li>
<li><a href="API.ru.html#api-design-annex">Глава 14. Приложение к разделу I. Модельный API</a></li>
</ul>
</li>
<li>
@@ -88,7 +88,7 @@
<li><a href="API.ru.html#api-patterns-async">Глава 19. Асинхронность и управление временем</a></li>
<li><a href="API.ru.html#api-patterns-lists">Глава 20. Списки и организация доступа к ним</a></li>
<li><a href="API.ru.html#api-patterns-push-vs-poll">Глава 21. Двунаправленные потоки данных. Push и poll-модели</a></li>
<li><a href="API.ru.html#api-patterns-async-event-processing">Глава 22. Мультиплексирование сообщений. Асинхронная обработка событий</a></li>
<li><a href="API.ru.html#api-patterns-async-event-processing">Глава 22. Мультиплексирование сообщений. Асинхронная обработка событий</a></li>
<li><a href="API.ru.html#chapter-23">Глава 23. Атомарность</a></li>
<li><a href="API.ru.html#chapter-24">Глава 24. Частичные обновления</a></li>
<li><a href="API.ru.html#chapter-25">Глава 25. Деградация и предсказуемость</a></li>
@@ -97,12 +97,12 @@
<li>
<h4><a href="API.ru.html#section-4">Раздел III. Обратная совместимость</a></h4>
<ul>
<li><a href="API.ru.html#back-compat-statement">Глава 26. Постановка проблемы обратной совместимости</a></li>
<li><a href="API.ru.html#back-compat-iceberg-waterline">Глава 27. О ватерлинии айсберга</a></li>
<li><a href="API.ru.html#back-compat-statement">Глава 26. Постановка проблемы обратной совместимости</a></li>
<li><a href="API.ru.html#back-compat-iceberg-waterline">Глава 27. О ватерлинии айсберга</a></li>
<li><a href="API.ru.html#back-compat-abstracting-extending">Глава 28. Расширение через абстрагирование</a></li>
<li><a href="API.ru.html#back-compat-strong-coupling">Глава 29. Сильная связность и сопутствующие проблемы</a></li>
<li><a href="API.ru.html#back-compat-weak-coupling">Глава 30. Слабая связность</a></li>
<li><a href="API.ru.html#back-compat-universal-interfaces">Глава 31. Интерфейсы как универсальный паттерн</a></li>
<li><a href="API.ru.html#back-compat-universal-interfaces">Глава 31. Интерфейсы как универсальный паттерн</a></li>
<li><a href="API.ru.html#back-compat-serenity-notepad">Глава 32. Блокнот душевного покоя</a></li>
</ul>
</li>
@@ -115,7 +115,7 @@
<li><a href="API.ru.html#chapter-36">Глава 36. Преимущества и недостатки HTTP API</a></li>
<li><a href="API.ru.html#chapter-37">Глава 37. Принципы организации HTTP API</a></li>
<li><a href="API.ru.html#chapter-38">Глава 38. Работа с ошибками в HTTP API</a></li>
<li><a href="API.ru.html#chapter-39">Глава 39. Организация URL ресурсов и операций над ними в HTTP API</a></li>
<li><a href="API.ru.html#chapter-39">Глава 39. Организация URL ресурсов и операций над ними в HTTP API</a></li>
<li><a href="API.ru.html#chapter-40">Глава 40. Заключительные положения и общие рекомендации</a></li>
</ul>
</li>
@@ -130,7 +130,7 @@
<li><a href="API.ru.html#chapter-46">Глава 46. MV*-фреймворки</a></li>
<li><a href="API.ru.html#chapter-47">Глава 47. Backend-Driven UI</a></li>
<li><a href="API.ru.html#chapter-48">Глава 48. Разделяемые ресурсы и асинхронные блокировки</a></li>
<li><a href="API.ru.html#chapter-49">Глава 49. Вычисляемые свойства</a></li>
<li><a href="API.ru.html#chapter-49">Глава 49. Вычисляемые свойства</a></li>
<li><a href="API.ru.html#chapter-50">Глава 50. В заключение</a></li>
</ul>
</li>
@@ -140,13 +140,13 @@
<li><a href="API.ru.html#api-product">Глава 51. Продукт API</a></li>
<li><a href="API.ru.html#api-product-business-models">Глава 52. Бизнес-модели API</a></li>
<li><a href="API.ru.html#api-product-vision">Глава 53. Формирование продуктового видения</a></li>
<li><a href="API.ru.html#api-product-devrel">Глава 54. Взаимодействие с разработчиками</a></li>
<li><a href="API.ru.html#api-product-business-comms">Глава 55. Взаимодействие с бизнес-аудиторией</a></li>
<li><a href="API.ru.html#api-product-range">Глава 56. Линейка сервисов API</a></li>
<li><a href="API.ru.html#api-product-devrel">Глава 54. Взаимодействие с разработчиками</a></li>
<li><a href="API.ru.html#api-product-business-comms">Глава 55. Взаимодействие с бизнес-аудиторией</a></li>
<li><a href="API.ru.html#api-product-range">Глава 56. Линейка сервисов API</a></li>
<li><a href="API.ru.html#api-product-kpi">Глава 57. Ключевые показатели эффективности API</a></li>
<li><a href="API.ru.html#api-product-antifraud">Глава 58. Идентификация пользователей и борьба с фродом</a></li>
<li><a href="API.ru.html#api-product-antifraud">Глава 58. Идентификация пользователей и борьба с фродом</a></li>
<li><a href="API.ru.html#api-product-tos-violations">Глава 59. Технические способы борьбы с несанкционированным доступом к API</a></li>
<li><a href="API.ru.html#api-product-customer-support">Глава 60. Поддержка пользователей API</a></li>
<li><a href="API.ru.html#api-product-customer-support">Глава 60. Поддержка пользователей API</a></li>
<li><a href="API.ru.html#api-product-documentation">Глава 61. Документация</a></li>
<li><a href="API.ru.html#api-product-testing">Глава 62. Тестовая среда</a></li>
<li><a href="API.ru.html#api-product-expectations">Глава 63. Управление ожиданиями</a></li>