admin / 09.12.2017
На днях кое-кто спросил меня, дескать, на кой черт вообще нужен этот REST. Зачем, например, заморачиваться с методом DELETE или там заголовком Accept? Не проще ли использовать метод GET и передавать все в параметрах, например, или ? Вбил в браузере, и работает! А вот этот ваш DELETE так просто через браузер не пошлешь. На что я ответил примерно так.
Вот, допустим, у вас есть некоторые ресурсы. Для определенности, пусть это будут книги и пользователи. Что, собственно, означает иметь REST API для работы с этими ресурсами? В первом приближении, следующее.
Если мы хотим получить какую-то книгу, то говорим . Аналогично информация о пользователе получается запросом . Вообще-то, в начале URL неплохо бы иметь что-то вроде , но для краткости мы это опустим. По умолчанию данные отдаются, например, в JSON’е, но при желании мы можем передать Accept-заголовок с другим форматом. Для создания или обновления существующей книги следует использовать метод PUT, передав данные в теле запроса и указав формат этих данных в заголовке Content-type. Для удаления данных используется метод DELETE.
Внимательный читатель спросит, а для чего тогда нужен POST? Вообще, если делать все по науке, он должен использоваться для добавления элементов в сущность, словно она является неким контейнером, например, словарем. Однако на практике так обычно не делают, ведь при использовании API несколькими клиентами один клиент может изменить название книги, а второй — ее цену, в результате чего получится ерунда. Поэтому POST либо вообще не используют, либо используют в качестве замены методов PUT и DELETE. То есть, POST с каким-то телом запроса работает, как PUT, а без тела запроса — как DELETE. Это позволяет работать с клиентами, которые почему-то не умеют посылать PUT и DELETE.
Можно работать и сразу с целыми коллекциями. Для получения списка всех пользователей говорим , а для создания нового пользователя с автоматически сгенерированным id — . Как и ранее, в последнем случае данные передаются в теле запроса. Также можно перезаписать всю коллекцию, сказав , и удалить сразу всех пользователей, сказав . Еще иногда требуется фильтрация по полям или пагинация, в этих случаях делают так:
GET /api/v1.0/users?fields=id,email,url&offset=100&limit=10&order_by=id
… или как-то так:
GET /api/v1.0/logs?from=2013-01-01+00:00:00&to=2013-12-31+23:59:59
Как бы, это все. Довольно однообразно и даже логично, не так ли? Так чем такой подход лучше описанного в начале поста?
В свое время я имел удовольствие работать над проектом, где API был устроен «простым и понятным» образом, на методах GET и POST, со всякими и так далее. Смею вас заверить, что на самом деле вы этого не хотите. Потому что на практике работа с этим API превращается в какой-то кошмар.
Допустим, один программист занимается книгами, а второй пользователями. Первый решает, что для получения списка всех сущностей будет использоваться запрос , а второй решает перечислять только id и использовать URL . Для удаления сущности первый программист решает использовать параметр , а второй — . Для экспорта данных в CSV первый программист делает поддержку , а второй — . Потом выясняется, что некоторые библиотеки не умеют посылать GET-запросы со слишком длинными query string и ходить за данными на чтение начинают методом POST. А затем кто-то случайно удаляет через браузер всех пользователей в боевом окружении… И так далее, и тому подобное, полный бардак в общем.
Вы спросите, что же мешает привести все это безобразие в одному стандарту, например, использовать только и ? Так вот, REST — это и есть то самое приведение к одному стандарту, с учетом всяческих граблей типа случайного удаления данных через браузер и так далее. Притом у разных компаний этот стандарт одинаковый. Когда в команду разработчиков приходит новичок, вы просто говорите ему, что у вас всюду REST, а основные ресурсы — это пользователи и книги. Все, после этого одного предложения ваш новый коллега знает 90% API, безо всякого там чтения Wiki. Если вы хотите говорить с иностранцами, вы же просто используете общепринятый английский язык, а не изобретаете новый? Вот так же и здесь. Нельзя также не напомнить о пользе повторного использования протоколов и кода. А ведь для работы с REST, и HTTP вообще, написана куча библиотек и фреймворков.
Вы скажите «я, конечно, согласен, что REST такой весь из себя интуитивно понятный и общепринятый, но что, если я просто хочу загрузить через браузер список книг в формате CSV»?
Тут важно понимать, что REST — это не о том, как сделать все через браузер. Предполагается, что должен быть клиент, который умеет работать с вашим API, вот через него и экспортируете. Но если по каким-то причинам это затруднительно, вы можете, например, использовать curl. Если у вас нелады с консолью, вы без труда найдете множество GUI-клиентов или, скажем, какой-нибудь плагин для Chrome, с аналогичным функционалом. Однако я все же советую попробовать curl. Пользоваться им совсем не так сложно, как вам может казаться. Всего-то нужно запомнить десяток параметров.
Так задаются дополнительные HTTP-заголовки:
-H ‘Accept: text/csv’ -H ‘Content-type: application/json’
Выбираем используемый метод:
-X{GET|PUT|POST|DELETE}
Указываем тело запроса:
-d ‘{"name":"Alex","url":"http://eax.me/"}’
Если тело запроса большое, можно сохранить его в файл и сказать:
-d @filename.json
# чтобы при этом не удалялись символы новой строки:
—data-binary @filename.json
Выводим заголовки из ответа сервера в stdout:
-D —
Говорим передавать данные в gzip’е:
—compressed
Сохраняем тело ответа в указанный файл вместо stdout:
-o output.json
Наконец, для отключения буферизации используйте флаг . Может пригодится, если вы работаете с большими объемами данных или бесконечными потоками.
Теперь рассмотрим пару примеров.
Экспорт книг в формате CSV:
curl -H ‘Accept: text/csv’ http://localhost/api/v1.0/books -o books.csv
Создание пользователя c выводом заголовков из ответа сервера в stdout:
curl -XPOST -H ‘Content-type: application/json’ -d ‘{"name":"Alex"}’ \
http://localhost/api/v1.0/users -D —
Удаление пользователя с заданным id:
curl -XDELETE http://localhost/api/v1.0/users/123
Несложно, правда ведь?
Несколько финальных замечаний, относящихся не совсем к REST.
Во-первых, иногда от приложения требуется не только предоставлять доступ к некоторым ресурсам, но и выполнять какие-то команды. Таким командам имеет смысл выделять URL-адреса, начинающиеся с . Например, запуск почтовой рассылки по всем пользователям будет выглядеть как-то так:
curl -XPOST -H ‘Content-type: application/json’ \
-d ‘{"subject":"Good news, everyone!","body":"…"}’ \
http://localhost/api/v1.0/commands/notify_all_users_via_email
Дополнение: Некоторые команды должны быть доступны только в тестовом окружении, для них можно выделить URL-адреса, начинающиеся с .
Во-вторых, иногда требуется реализовать бесконечные потоки событий, или отправку текущего состояния, а затем обновлений к нему. Таким концам разумно выделить URL, начинающиеся, например, со . Вот как примерно это должно работать:
curl -H ‘Accept: application/x-json-stream’ \
http://localhost/api/v1.0/streams/users -N
{"type":"user","data":{"id":123,"name":"Alex","url":"http://eax.me/"}}
{"type":"user","data":{"id":456,"name":"Bob","url":"http://ya.ru/"}}
…
{"type":"sync"}
{"type":"heartbeat"}
{"type":"heartbeat"}
{"type":"user_deleted","data":{"id":123}}
…
Нужно обратить внимание на несколько моментов. Здесь используется формат , то есть, поток JSON-объектов, разделенных символом \n. Если этот символ встречается в самом JSON-объекте, его, соответственно, следует кодировать. Некоторым клиентам может быть удобнее работать с честным JSON’ом, то есть, списком JSON-объектов. Предусмотреть поддержку сразу нескольких форматов довольно просто. Во втором случае список объектов должен начинаться с открывающейся квадратной скобки, а объекты должны разделяться запятыми. Для удобства работы со стримом нужно либо ставить после запятых символ \n, либо делать это на стороне клиента с помощью sed:
curl … | sed ‘s/},/}\n/g’
Каждый объект имеет поле type и опциональное поле data. Объекты с типом heartbeat посылаются несмотря ни на что один раз в пять секунд. Если клиент не видит такого объекта в течение десяти секунд, он считает, что либо что-то сломалось на стороне сервера, либо что-то не так с сетью, и закрывает соединение. Объект с типом sync используется в стримах, посылающих некое состояние, а затем обновления к нему, для разделения первого от второго. Наконец, все остальные типы представляют собой полезную нагрузку. Поле data нужно по той причине, что вложенные данные также могут иметь поле type, что приводило бы к неразберихе.
В-третьих, когда вы пишите RESTful приложение, старайтесь с самого начала придерживаться некоторых соглашений. Например, с самого начала договоритесь, что имена полей в JSON-объектах должны всегда писаться в camelCase. Раз и навсегда запретите использовать в идентификаторах такие спецсимволы, как знак плюс и пробелы. Договоритесь, что в случае получения кода 301 клиент должен посылать точно такой же запрос на URL, указанный в заголовке Location. Примите соглашение о том, как будет передаваться автоматически сгенерированные id. Например, в Riak для этого используется заголовок Location. Подумайте о том, как вы будете сообщать о различных типах ошибок, в том числе временной недоступности БД, ошибках валидации полей и так далее. Пользователи почти наверняка предпочтут увидеть:
{"message":"validation_error","description":"…"}
… вместо кода 500 без каких-либо дополнительных пояснений. Если для вашего приложения важна точность представления чисел, договоритесь передавать все числа в виде строк, чтобы json-декодер не терял точность из-за преобразования строк во float’ы.
Но помните, хотя все написанное выше — это идеал, к которому стоит стремиться, на практике всем наплевать на стандарты. А значит, вас ждет много подпорок, слепленных на скорую руку, нежелание коллег переходить на более правильные версии API (зачем, если все работает?), и многие другие увлекательные вещи.
Дополнение:Пишем REST-сервис на Python с использованием Flask
Метки: Протоколы, Разработка.
В данной статье я расскажу Вам о том, что такое REST API. Также мы затронем тему HTTP протокола.А также рассмотрим пример архитектурного дизайна REST API.
Немного теории
О том, что такое API, я подробно рассказывал здесь. Напомню, что API – это некий набор правил, с помощью которых приложение или какой-либо один его компонент могут взаимодействовать, общаться, если хотите, с другим приложением или компонентом.
Прикладной интерфейс программирования (API) может возвращать данные в разных форматах, например в JSON, XML или в бинарном формате, но в REST API мы будем использовать JSON-формат, как наиболее удобный.
Давайте посмотрим на пример. Возможно, Вы уже знакомы с тем, что такое система контроля версий Git. Ее web-версия – это Github. Так вот, у Github есть собственное API, с помощью которого можно получить какую-либо полезную информацию, например о пользователях и организациях, их проектах, и т.д. Давайте взглянем на пример:
В этом примере мы используем консольную утилиту curl для того, чтобы получить данные через API. Ее можно загрузить с официального сайт проекта. Она позволяет делать все то же самое что и расширение curl в PHP, только для этого не нужно писать код, так как вся функциональность доступна посредством интерфейса командной строки. Вообще, незаменимая вещь для тестирования различных прикладных интерфейсов. Есть еще альтернатива в виде расширения для Chrome – Postman.
Данная команда вернет нам большой JSON-объект, содержащий различные данные о компании.
Теперь остановимся подробнее на том, что же такое REST. Это сокращение может быть расшифровано в следующем виде: представление данных для клиента в формате удобном для него. Очень важно запомнить, что REST – это не протокол, а подход, архитектурный стиль к написанию прикладных интерфейсов.
Если говорить еще проще то, REST – это архитектурный стиль, а RESTful API – это его практическое воплощение, и чем больше приложение отвечает критериям стиля REST, тем более оно RESTful.
RESTful API сводится к четырем базовым операциям:
REST функционирует поверх протокола HTTP, поэтому стоит упомянуть о его основных особенностях.
Для каждой операции указанной выше используется свой собственный HTTP метод:
Все эти методы в совокупности называют CRUD (create, read, update, delete) – (создать, прочитать, обновить, удалить) операциями.
Фактически в REST существует единственный, непротиворечивый общий интерфейс для запросов, например, к базам данных, что является его важнейшим преимуществом. На следующей картинке показано соответствие HTTP методов SQL операциям и концепции CRUD.
Т.е. HTTP метод POST соответствует SQL операции INSERT, метод GET – операции SELECT и т.д.
Для каждого HTTP запроса есть свой статус. И они нужны, чтобы грамотно с точки зрения REST API оформить ответ и отдать клиенту. Статусов много, поэтому их всех не перечислить, однако важно знать их группировку:
Вообще, как делается API. Создается некая точка входа для запросов, api.php, например. Этому API, могут передаваться, например, такие запросы:
где параметр
Однако этот подход, несмотря не некую простоту, не лишен недостатков, хотя бы потому, что для всех видов запросов используется один метод GET, тогда как в спецификации HTTP их определено с десяток, каждый для своей конкретной области.
Сегодня REST API используется повсюду, начиная от сайтов, заканчивая мобильными приложениями, поэтому важно знать как работать с ним, так как рано или поздно может возникнуть необходимость в создании клиента (мобильного приложения,например) для своего сайта или того же блога.
Таким образом, REST API призван создать четко структурированный подход в написании прикладных интерфейсов. Так, как с каждым днем становится все больше и больше данных, к которым необходимо открыть доступ.
Создано 17.10.2017 08:21:19
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Она выглядит вот так:
Она выглядит вот так: Как создать свой сайт
Так как REST это акроним для REpresentational State Transfer, отсутствие состояний является важной чертой. Таким образом, это значит, что необходимое состояние для обработки запроса содержится в самом запросе, либо в рамках URI, параметрах строки запроса, тела или заголовках. URI уникально идентифицирует ресурс и тело содержит состояние (или изменение состояния) этого ресурса. Затем, после того, как сервер завершит обработку, состояние или его часть(и) отдается обратно клиенту через заголовки, статус и тело ответа.
Большинство из нас, кто был в этой отрасли, привыкли к программированию в контейнере, который дает нам понятие «Сессия, которая поддерживает состояние нескольких HTTP запросов.
В REST, клиент должен включать всю информация для сервера для выполнения запроса, перепосылая состояние по необходимости, если это состояние должно охватывать несколько запросов. Отсутствие состояний обеспечивает большую масштабируемость, так как сервер не должен поддерживать или общаться через состояние сеанса. Кроме того, балансировщику нагрузки не придется беспокоиться о связанности сессии и системы.
Так в чем различие между состоянием и ресурсом? Состояние или состояние приложения, это то, что сервер заботится выполнить запрос для получения данных необходимых для текущей сессии или запроса. Ресурсное состояние, или ресурс, это данные, которые определяют представление ресурса, например, данные хранящиеся в базе данных. Рассмотрим состояние приложения как данные, которые могут варьироваться в зависимости от клиента и запроса. С другой стороны, состояние ресурсов постоянно по каждому клиенту, который запрашивает его.
Каждый встречал проблему с кнопкой «Назад» в своем веб приложении, когда оно ведет себя по разному в одной точке, потому что ожидались действия в определенном порядке? Такое происходит, когда нарушен принцип отсутствия состояний. Есть случаи, когда не соблюдается принцип отсутствия состояний, например, three-legged OAuth, ограничение скорости вызова API и т.д. Однако, приложите максимум усилий, чтобы состояние приложения не занимало несколько запросов к вашему сервису.
FILED UNDER : IT