Что такое REST API?

От автора: что такое REST API? REST – это аббревиатура Representational State Transfer или по-русски «передача репрезентативного состояния» — вообще непонятное описание самой используемой технологии в веб-сервисах! REST API – это способ для двух компьютеров взаимодействовать через HTTP в браузерах и серверах.

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

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

REST повсеместно используется в веб-системах для взаимодействия. Например, получение и обновление данных профиля в социальной сети.

Пример REST API

Перейдите по следующий ссылке, в ответ вы получите случайный вопрос по компьютерам с портала Open Trivia Database.

Это публичный API, реализованный как RESTful веб-сервис (следует правилам REST). В браузере отобразится один вопрос с ответом в формате JSON, как на примере ниже:

{ "response_code": 0, "results": [ { "category": "Science: Computers", "type": "multiple", "difficulty": "easy", "question": "What does GHz stand for?", "correct_answer": "Gigahertz", "incorrect_answers": [ "Gigahotz", "Gigahetz", "Gigahatz" ] } ] }

Вы можете выполнить этот запрос и получить ответ в любом HTTP-клиенте, например curl:

curl "https://opentdb.com/api.php?amount=1&category=18"

Библиотеки HTTP-клиентов доступны на всех популярных языках программирования и рантаймах, в том числе Fetch для JS, Node.js и Deno, а также file_get_contents() для PHP. JSON ответ машиночитаемый, т.е. его можно распарсить перед выводом в HTML или другой формат.

REST API и Rest

За последние годы развилось множество стандартов передачи данных. Вы могли слышать про CORBA, SOAP или XML-RPC. Большинство стандартов устанавливают строгие правила передачи сообщений.

REST придумал Roy Fielding в 2000 году. Его подход значительно проще остальных. Это не стандарт, а набор рекомендаций и ограничений для RESTful сервисов. Выделяют следующие правила:

Клиент-сервер: система А делает HTTP запрос на URL, по которому развернута система B. Система B отвечает. Принцип идентичен работе браузера. Браузер посылает запрос по URL. Запрос направляется на веб-сервер, который обычно отвечает HTML-страницей. Страница может содержать ссылки на изображения, стили и JS. Все это ведет к последующим запросам и ответам.

Нет состояния: REST не хранит состояний: запрос от клиента должен содержать всю информацию, необходимую для ответа. Другими словами, должна быть возможность посылать 2 и более HTTP запросов в любом порядке, а в ответ должен приходить одинаковый ответ (только если API специально не спроектировано отдавать случайные респонсы, как в примере с вопросами выше).

Кэшируемость: респонс должен помечаться как кэшируемый или нет. Кэширование улучшает производительность, позволяя не генерировать повторно ответ на такой же URL. Однако приватные данные пользователей в определенные моменты лучше не кэшировать.

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

Создание RESTful веб-сервиса

Запрос RESTful сервиса содержит:

Эндпоинт URL. Приложение, реализующее RESTful API, определяет один или более URL эндпоинтов с доменом, портом, самим эндпоинтом и/или строкой параметров. Например, https://mydomain/user/123?format=json

HTTP метод. Любой эндпоинт может использовать различные HTTP методы, которые ведут в приложении к операциям создания, чтения, обновления и удаления данных (CRUD):

Что такое REST API?

Примеры:

GET запрос к /user/ вернет список зарегистрированных пользователей в системе

POST запрос к /user/ создаст нового юзера с ID 123 на основе переданных данных (см пункт 4 далее). В ответ мы получим ID

PUT запрос к /user/123 обновит юзера 123 данными, которые мы передали (см пункт 4 далее)

GET запрос к /user/123 вернет информацию по юзеру 123

DELETE запрос к /user/123 удалит юзера 123

Также запрос RESTful сервиса содержит:

HTTP заголовки. Информация типа токенов авторизации или куки может храниться в HTTP заголовках запроса.

Тело запроса. Данные обычно передаются в HTTP теле аналогично передаче данных через HTML form или передаче данных через JSON.

Что такое REST API?

REST API ответ

Ответ может содержать все что угодно: данные, HTML, изображений, аудио файл итд. Обычно ответ кодируют в JSON, но бывают ответы в XML, CSV, в виде простых строк итд. Возвращаемый формат можно указать в самом запросе. Например, /user/123?format=json или /user/123?format=xml.

В хедерах ответа также необходимо указать подходящий HTTP статус код. 200 OK используется для успешных запросов, 201 Created можно вернуть при создании записи. У ошибок свои коды, например, 400 Bad Request, 404 Not Found, 401 Unauthorized и так далее.

Другие хедеры можно указать через директивы Cache-Control или Expires. С их помощью можно указать время кэширования.

Тем не менее, нет строгих правил. Эндпоинты, HTTP методы, тело и типы ответов можно реализовывать по-своему. Например, POST, PUT и PATCH часто используют так, что они становятся взаимозаменяемыми и через любой из них можно создать или обновить запись.

Пример Hello World REST API

Node.js код ниже создает RESTful сервис на фреймворке Express. У этого приложения есть один эндпоинт GET /hello/. Установите Node.js, создайте папку restapi. Внутри папки создайте package.json:

{ "name": "restapi", "version": "1.0.0", "description": "REST test", "scripts": { "start": "node ./index.js" }, "dependencies": { "express": "4.18.1" } }

Выполните команду npm install в командной строке, чтобы загрузить зависимости. Далее создайте файл index.js:

// simple Express.js RESTful API 'use strict'; // initialize const port = 8888, express = require('express'), app = express(); // /hello/ GET request app.get('/hello/:name?', (req, res) => res.json( { message: `Hello ${req.params.name || 'world'}!` } ) ); // start server app.listen(port, () => console.log(`Server started on port ${port}`); );

Запустите приложение через командную строку npm start. Откройте в браузере http://localhost:8888/hello/. На экране отобразится JSON:

{ "message": "Hello world!" }

API позволяет передавать имя. То есть запрос на http://localhost:8888/hello/everyone вернет:

{ "message": "Hello everyone!" }

Клиентские REST запросы и CORS

Рассмотрим HTML-страницу ниже, запущенную в браузере по URL http://localhost:8888:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>REST test</title> </head> <body> <script> fetch('http://localhost:8888/hello/') .then((response) => { return response.json(); }) .then((json) => { console.log(json); }); </script> </body> </html>

Вызов fetch делает такой же API запрос, а в консоли можно увидеть ответ Object { message: «Hello world!» }.

Предположим, теперь наш RESTful сервис выложили в интернет на реальный домен http://mydomain.com/hello/. URL для fetch изменился. Но если открыть http://localhost:8888 в браузере, мы получим ошибку в консоли Cross-Origin Request Blocked.

По соображениям безопасности браузеры разрешают клиентам выполнять XMLHttpRequest и Fetch API запросы только на тот же домен, откуда идет сам запрос.

К счастью, Cross-origin Resource Sharing (CORS) позволяет нам обойти это ограничение. Заголовок ответа Access-Control-Allow-Origin скажет браузеру, куда он может посылать запросы. В значении можно указать конкретный домен или * для любого домена (как сделано в API из примера выше).

Код API можно изменить, чтобы сервис мог отвечать любому клиенту на любом домене:

// /hello/ GET request app.get('/hello/:name?', (req, res) => res .append('Access-Control-Allow-Origin', '*') .json( { message: `Hello ${req.params.name || 'world'}!` } ) );

Или можно через Express.js middleware функцию добавить наш хедер во все запросы:

// enable CORS app.use((req, res, next) => { res.append('Access-Control-Allow-Origin', '*'); next(); }); // /hello/ GET request // ...

Обратите внимание, что браузеры делают два запроса к REST API:

Запрос OPTIONS на тот же адрес, чтобы определить правильность хедера Access-Control-Allow-Origin

Сам REST запрос

Чтобы не дублировать код на запрос OPTIONS можно возвращать хедер Access-Control-Allow-Origin с пустым ответом.

Сложности в REST API

Успех REST в его простоте. Разработчики сами пишут RESTful API, как хочется им. Однако это может привести к некоторым сложностям. Для углубленного изучения стратегий архитектуры API ознакомьтесь с нашим уроком «13 лучших практик по созданию RESTful API».

Консенсус по эндпоинтам

Разберем следующие эндпоинты:

/user/123

/user/id/123

/user/?id=123

Все варианты подходят для получения данных для юзера 123. Количество комбинаций будет расти вместе со сложностью операций. Например, вернуть 10 юзеров, чьи фамилии начинаются на А, и кто работает на компанию Х, начиная с записи 51 с сортировкой по дате рождения в обратном от хронологического порядке.

Абсолютно неважно, как вы будете форматировать URL, однако консистентность во всем API очень важна. В большом проекте, где много разработчиков, это может вызвать некоторые сложности.

Версирование REST API

Изменения в API неминуемы, но нельзя ломать существующие URL. Иначе это приведет к поломке приложения.

Чтобы избежать проблем совместимости, API зачастую устанавливают версию. Например, /2.0/user/123 заменяет /user/123. Оба эндпоинта могут оставаться активными. Такой подход заставляет поддерживать несколько версий API. В будущем старые версии можно удалить, но этот процесс нужно тщательно спланировать.

REST API аутентификация

API из примера в начале статьи открытый: любая система может получить от него шутку без авторизации. Для API с доступом к приватным данным с возможностью обновлять и удалять эти данные такой подход не применим.

Клиентские приложения в том же домене что и RESTful API будут отправлять и получать куки, как любой другой HTTP запрос. (обратите внимание, что Fetch() в старых браузерах требует credentials init опцию) API запрос можно провалидировать на авторизацию юзера и на наличие подходящих прав пользователя.

Сторонние приложения должны использовать альтернативные способы авторизации. Среди часто используемых способов аутентификации выделяют:

HTTP basic аутентификацию. HTTP заголовок Authorization со строкой username:password, закодированной в base64.

API ключи. Сторонним приложениям разрешается использовать API с помощью выпущенного ключа, у которого могут быть определенные права и ограничения к отдельным доменам. Ключ передается во всех запросах в заголовках или в строке запроса.

OAuth. Перед работой с API требуется получить токен. Для этого отправляется запрос с ID клиента и возможно с секретным ключем на OAuth сервер. Далее токен отправляется в каждом API запросе до тех пор, пока он не прекратит действие.

JSON веб токены (JWT). Токены аутентификации подписаны цифровой подписью и безопасно передаются в хедерах запроса и ответа. JWT позволяет серверу кодировать права доступа, из-за этого не требуются дополнительные обращения в базу данных или системы авторизации.

API аутентификация выбирается исходя из контекста:

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

В других случаях стороннее приложение может запрашивать приватные данные юзера, например, электронную почту. REST API должно определить юзера и его права, и ему будет безразлично, какое приложение его вызывает.

Безопасность REST API

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

Безопасность выходит за рамки нашей статьи, но можно перечислить общепринятые лучшие практики:

Используйте HTTPS

Используйте надежный метод аутентификации

Ограничивайте количество клиентских запросов к определенным доменам через CORS

Предоставляйте минимум функциональности – не создавайте DELETE метод, если он не нужен

Валидируйте все эндпоинты и тело запросов

Не храните API токены в клиентском JS

Блокируйте доступ с неизвестных доменов и ip-адресов

Блокируйте неожиданно большие нагрузки

Подумайте о лимите на запросы – ограничить запросы под одним API токеном или IP до N количества в минуту

Отвечайте подходящими статус кодами и кэшируйте хедеры

Логируйте запросы и исследуйте проблемы

Множественные запросы и ненужные данные

RESTful APIs ограничены лишь реализацией. В ответе может быть больше данных, чем необходимо. Или же для доступа ко всем данным необходимо посылать дополнительные запросы.

Рассмотрим RESTful API, предоставляющим доступ к данным автора и его книг. Для получения данных о топ-10 продаваемых книг клиент может:

Запросить первые 10 /book/ отсортированные по количеству продаж (самая продаваемая сверху). В ответе может содержаться список книг с ID авторов.

Выполнить 10 запросов /author/{id} для получения данных по каждому автору.

Это известная проблема, ее называют проблемой N+1. Для родительского запроса нужно выполнить N API запросов для каждого результата.

Если это распространенный пример, то RESTful API можно изменить таким образом, чтобы в каждой возвращаемой книге была вся информация по автору: имя, возраст, страна, биография итд. В ответе даже может храниться информация по его другим книгам – но это увеличит респонс!

Без необходимости не раздувайте респонсы. API можно настроить так, чтобы детали по автору были необязательным параметром. Например, ?author_details=full. Количество опций, которые нужно учесть автору API, может сбить с толку.

GraphQL фиксит REST API?

Сложные задачки с REST навели на мысль создать GraphQL – язык запросов веб-сервисов. Представьте, что это SQL для веб-сервисов: строка запроса определяет возвращаемые данные и их формат.

GraphQL решает некоторые проблемы RESTful APIs, но и вносит новые сложности. Например, сложнее кэшировать GraphQL респонсы.

Возможно, стоит подумать о GraphQL, когда ваш RESTful API выходит за пределы своего практического применения.

Ссылки по REST API и инструмент для разработки

Существует множество инструментов для разработки RESTful API на всех языках. Выделить можно:

Swagger: различные инструменты для проектирования, документирования, написания заглушек и мониторинга REST API

Postman: приложение для тестирования RESTful API

Hoppscotch: open-source веб альтернатива Postman

Также есть много публичных REST API для генерации шуток, конвертации валют, геокодирования, получения гос. данных и еще много всего, что сложно представить. Множество из них бесплатны, часть требует авторизации для получения API ключа или для авторизации через другие методы. Списки по категориям:

Перед написанием своего веб-сервиса попробуйте использовать парочку RESTful API в своем проекте. Или последуйте примеру GitHub, Google и других гигантов индустрии и постройте свой RESTful API.

Автор: Craig Buckler

Источник: www.sitepoint.com

Редакция: Команда webformyself.

Читайте нас в Telegram, VK, Яндекс.Дзен