Руководство для начинающих по HTTP и REST...
Протокол передачи гипертекста (HTTP) - это основа жизни в Интернете. Он используется каждый раз при передаче документа или в запросе AJAX
. Но HTTP на удивление мало известен некоторым веб-разработчикам.
В этом введении мы познакомимся с принципами разработки REST, лежащими в основе HTTP и использованию их мощи для создания интерфейсов, которые могут выполняться практически с любого устройства или операционной системы.
Каждые несколько недель мы пересматриваем некоторые из любимых сообщений наших читателей за всю историю сайта. Этот урок впервые был опубликован в ноябре 2010 года.
Почему REST?
REST - простой способ организации взаимодействия между независимыми системами.
REST - простой способ организации взаимодействия между независимыми системами. Он пользуется популярностью с 2005 года и вдохновляет дизайн сервисов, таких как API Twitter. Благодаря тому, что REST обеспечивает взаимодействие с такими разнообразными клиентами, как мобильные телефоны и другие веб-сайты. Теоретически, REST не привязан к сети, но почти всегда реализован как таковой и был вдохновлен HTTP. В результате REST можно использовать везде, где возможен HTTP.
Альтернативой является создание относительно сложных соглашений поверх HTTP. Часто это принимает форму новых XML-языков. Самый яркий пример - SOAP. Вам нужно выучить совершенно новый набор соглашений, но вы никогда не используете HTTP в полную силу. Поскольку REST был вдохновлён HTTP и играет на его сильные стороны, это лучший способ узнать, как работает HTTP.
После первоначального обзора мы рассмотрим каждый из строительных блоков HTTP: URL-адреса, HTTP-команды и коды ответов. Мы также рассмотрим, как использовать их в RESTful. Попутно мы проиллюстрируем теорию примером приложения, которое имитирует процесс отслеживания данных, связанных с клиентами компании через веб-интерфейс.
HTTP
HTTP - это протокол, который позволяет отправлять документы в Интернете.
HTTP - это протокол, который позволяет отправлять документы в Интернете. Протокол - это набор правил, определяющих, какие сообщения можно обменивать, и какие сообщения являются подходящими ответами для других. Другим распространенным протоколом является POP3, который вы можете использовать для извлечения электронной почты на вашем жёстком диске.
В HTTP есть две разные роли: сервер и клиент. Как правило, клиент всегда инициирует разговор; сервер отвечает. HTTP основан на тексте; то есть сообщения по сути являются битами текста, хотя тело сообщения может также содержать другие носители. Использование текста позволяет легко отслеживать обмен HTTP.
HTTP-сообщения состоят из заголовка и тела. Тело часто может оставаться пустым; оно содержит данные, которые вы хотите передать по сети, чтобы использовать их в соответствии с инструкциями в заголовке. Заголовок содержит метаданные, например информацию о кодировке; но, в случае запроса, он также содержит важные HTTP-методы. В стиле REST вы обнаружите, что данные заголовка часто более значимы, чем тела.
Шпионство HTTP на работе
Если вы используете Chrome Developer Tools или Firefox с расширением Firebug, щелкните на панели Net
и установите его в enabled
. После этого у вас будет возможность просматривать информацию о HTTP-запросах по мере вашего поиска. Например:
Другим полезным способом ознакомиться с HTTP является использование выделенного клиента, такого как cURL.
cURL - это инструмент command line, который доступен во всех основных операционных системах.
После установки cURL введите:
curl -v google.com
Будет отображен полный диалог HTTP. Запросам предшествует >
, а ответам предшествует <
.
URLS
URL - это, как вы определяете то, на чём вы хотите работать. Мы говорим, что каждый URL-адрес идентифицирует ресурс. Это те же самые URL-адреса, которые назначены веб-страницам. Фактически, веб-страница - это тип ресурса. Давайте рассмотрим более экзотический пример с нашим образцом приложения, которое управляет списком клиентов компании:
/clients
будет идентифицировать всех клиентов, в то время как
/clients/jim
определяет клиента с именем "Jim", предполагая, что он единственный с таким именем.
В этих примерах мы обычно не включаем hostname в URL, так как это не имеет никакого отношения к организации интерфейса. Тем не менее, hostname важно для того, чтобы идентификатор ресурса был уникальным во всей сети. Мы часто говорим, что вы отправляете запрос на ресурс к host. Хост включается в заголовок отдельно от пути ресурса, который идёт прямо над заголовком запроса:
GET /clients/jim HTTP/1.1Host: example.com
Ресурсы лучше всего рассматривать как существительные. Например, следующее не RESTful:
/clients/add
Это связано с тем, что для описания действия используется URL-адрес. Это довольно фундаментальный момент в различиях систем RESTful от систем без RESTful.
Наконец, URL-адреса должны быть максимально точными; всё, что необходимо для уникальной идентификации ресурса, должно быть в URL-адресе. Вам не нужно включать в запрос данные, идентифицирующие ресурс. Таким образом, URL-адреса выступают в качестве полной карты всех данных, обрабатываемых приложением.
Но как вы определяете действие? Например, как сообщить, что вы хотите создать новую запись клиента вместо её получения? Именно здесь вступают в действие глаголы HTTP.
Глаголы HTTP
Каждый запрос указывает определенный HTTP глагол или метод в заголовке запроса. Это первое слово в заголовке запроса. Например,
GET / HTTP/1.1
означает, что используется метод GET, а
DELETE /clients/anne HTTP/1.1
означает использование метода DELETE
.
Глаголы HTTP сообщают серверу, что делать с данными, указанными URL.
Глаголы HTTP сообщают серверу, что делать с данными, указанными URL. Запрос может ещё содержать дополнительную информацию в своем теле, которая может потребоваться для выполнения операции - например, данные, которые вы хотите сохранить с ресурсом. Эти данные можно указать в cURL с параметром -d
.
Если вы когда-либо создавали HTML-формы, то знакомы с двумя из наиболее важных HTTP-глаголов: GET
и POST
. Но есть гораздо больше доступных HTTP-глаголов. Наиболее важными для построения RESTful API являются GET
, POST
, PUT
и DELETE
. Доступны другие методы, такие как HEAD
и OPTIONS
, но они более редки (если вы хотите знать обо всех других методах HTTP, официальным источником является IETF).
GET
GET
- это самый простой тип HTTP-запроса; которым браузер пользуется каждый раз, когда вы нажимаете ссылку или вводите URL-адрес в адресную строку. Он инструктирует сервер передавать клиенту данные, идентифицированные URL-адресом. Никогда не последует изменений данных на стороне сервера в результате запроса GET
. В этом смысле GET
-запрос доступен только для чтения, но, конечно, как только клиент получит данные, он может самостоятельно выполнять любые операции с ними, например, форматировать для отображения.
PUT
Запрос PUT
используется, когда вы хотите создать или обновить ресурс, указанный URL-адресом. Например,
PUT /clients/robin
может создать клиента с именем Robin
на сервере. Вы заметите, что REST
полностью независим от бэкэнда; в запросе нет ничего, что информирует сервер о том, как данные должны создаваться - просто так должно быть. Это позволяет вам легко менять базовую технологию по необходимости. Запросы PUT
содержат данные для использования при обновлении или создании ресурса в body. В cURL вы можете добавить данные в запрос с помощью -d
.
curl -v -X PUT -d "some text"
DELETE
DELETE
должен выполнять противоположное PUT
; его следует использовать, если вы хотите удалить ресурс, указанный URL-адресом запроса.
curl -v -X DELETE /clients/anne
Это приведёт к удалению всех данных, связанных с ресурсом, идентифицированных /clients/anne
.
POST
POST
используется, когда обработка, которую вы хотите выполнить на сервере, должна повторяться, если запрос POST
повторяется (то есть, они не являются идемпотентными, подробнее об этом ниже). Кроме того, POST
-запросы должны вызывать обработку body запроса как подчинённого URL-адреса, который вы отправляете.
Проще говоря:
POST /clients/
не должен вызывать изменение ресурса в /clients/
сам, но ресурс URL начинается с него /clients/
. Например, он может добавить в список нового клиента, с id
, сгенерированным сервером.
/clients/some-unique-id
Запросы PUT
легко используются вместо запросов POST
и наоборот. Некоторые системы используют только один, некоторые используют POST
для создания операций и PUT
для операций обновления (поскольку с запросом PUT
вы всегда указываете полный URL-адрес), некоторые используют POST
для обновлений и PUT
для создания.
Часто запросы POST
используются для запуска операций на сервере, которые не вписываются в парадигму Create/Update/Delete
; но это, однако, выходит за рамки REST
. В нашем примере мы будем полностью придерживаться PUT
.
Классификация методов HTTP
GET
. Другие небезопасны, так как они могут привести к модификации ресурсов. GET
, PUT
и DELETE
. Единственным неидемпотентным методом является POST
. PUT
и DELETE
, считающиеся идемпотентами, могут быть неожиданными, хотя, на самом деле, это довольно легко объяснить: повторение метода PUT
с точно таким же body должно модифицировать ресурс таким образом, чтобы он оставался идентичным описанному в предыдущем запросе PUT
: ничего не изменится! Аналогичным образом, нет смысла дважды удалять ресурс. Из этого следует, что независимо от того, сколько раз запрос PUT
или DELETE
повторяется, результат должен быть таким же, как если бы это было сделано только один раз.Помните: именно вы, программист, в конечном счете решаете, что происходит, когда используется определённый HTTP-метод. В реализациях HTTP нет ничего, что автоматически приведёт к созданию ресурсов, их перечислению, удалению или обновлению. Вы должны быть осторожны, чтобы правильно применять HTTP-протокол и вводить эту семантику самостоятельно.
Представительства
HTTP-клиент и HTTP-сервер обмениваются информацией о ресурсах, определённых URL-адресами.
Мы можем суммировать то, что мы узнали до сих пор, следующим образом: HTTP-клиент и HTTP-сервер обмениваются информацией о ресурсах, определённых URL-адресами.
Мы говорим, что запрос и ответ содержат представление ресурса. Под представлением мы подразумеваем информацию в определённом формате о состоянии ресурса или о том, каким это состояние должно быть в будущем. Оба, и header, и body - являются частями представления.
Заголовки HTTP, содержащие метаданные, жёстко определяются спецификацией HTTP; они могут содержать только простой текст и должны быть отформатированы определённым образом.
Тело может содержать данные в любом формате, и именно здесь видна сила HTTP. Вы знаете, что можете отправлять простой текст, изображения, HTML и XML на любом человеческом языке. Через метаданные запроса или различные URL-адреса вы можете выбирать между различными представлениями для одного и того же ресурса. Например, вы можете отправить веб-страницу в браузеры и JSON в приложения.
HTTP-ответ должен указывать тип содержимого body. Это делается в заголовке, в поле Content-Type; например:
Content/Type: application/json
Для простоты наше приложение только отправляет JSON туда и обратно, но приложение должно быть спроектировано таким образом, чтобы вы могли легко изменять формат данных, чтобы адаптироваться для разных клиентов или предпочтений пользователя.
Библиотеки клиента HTTP
cURL - это, чаще всего, HTTP-решение для PHP-разработчиков.
Чтобы поэкспериментировать с различными методами запроса, вам нужен клиент, который позволит указать, какой метод использовать. К сожалению, формы HTML не подходят для счёта, так как они позволяют делать только запросы GET и POST. В реальной жизни API-интерфейсы доступны программно через отдельное клиентское приложение или через JavaScript в браузере.
Именно поэтому в дополнение к серверу важно иметь хорошие возможности HTTP-клиента на выбранном вами языке программирования.
Очень популярная клиентская HTTP-библиотека, опять же, cURL. Вы уже были ознакомлены с командой cURL ранее в этом уроке. CURL включает в себя как автономную программу командной строки, так и библиотеку, которая может использоваться различными языками программирования. В частности, cURL является, чаще всего, идеальным решением HTTP-клиента для разработчиков PHP. Другие языки, такие как Python, предлагают больше собственных клиентских HTTP-библиотек.
Настройка примера приложения
Я хочу показать как можно более низкий уровень функциональности.
Наш пример PHP-приложения чрезвычайно тощ. Я хочу выставить как можно более низкоуровневую функциональность, без какой-либо магии framework . Я также не хотел использовать настоящий API, например, Twitter, потому что они могут неожиданно меняться, вам нужно настроить аутентификацию, что может быть проблемой и вы не сможете изучить реализацию.
Чтобы запустить пример приложения, вам необходимо установить PHP5 и веб-сервер с механизмом для запуска PHP. Текущая версия должна быть не ниже версии 5.2, чтобы иметь доступ к функциям json_encode ()
и json_decode ()
.
Что касается серверов, наиболее распространенным вариантом является Apache с mod_php, но вы можете использовать любые альтернативы, которые вам удобны. Существует пример конфигурации Apache, который содержит правила перезаписи, которые помогут вам быстро настроить приложение. Все запросы к любому URL, начиная с /clients/, должны быть направлены в наш файл server.php.
В Apache вам нужно включить mod_rewrite и поместить прилагаемую конфигурацию mod_rewrite где-нибудь в вашей конфигурации Apache или в ваш файл .htacess. Таким образом, server.php будет отвечать на все запросы, поступающие с сервера. То же самое должно быть достигнуто с Nginx, или с любым другим сервером, который вы решите использовать.
Как работает пример приложения
Есть два ключа по обработке запросов REST. Первый ключ - инициировать различную обработку, в зависимости от метода HTTP, даже когда URL-адреса одинаковы. В PHP в глобальном массиве $ _SERVER есть переменная, которая определяет, какой метод был использован для выполнения запроса:
$_SERVER["REQUEST_METHOD"]
Эта переменная содержит имя метода в виде строки, например "GET
", "PUT
" и далее.
Другой ключ - узнать, какой URL был запрошен. Для этого мы используем другую стандартную переменную PHP:
$_SERVER["REQUEST_URI"]
Эта переменная содержит URL-адрес, начинающийся с первой косой черты. Например, если имя хоста - "example.com
", "http://example.com/
" вернётся "/
", как "http://example.com/test/
" вернётся "/test/
".
Давайте сначала попытаемся определить, какой URL-адрес был вызван. Мы рассматриваем только URL-адреса, начинающиеся с "clients
". Все остальные недействительны.
$resource = array_shift($paths);if ($resource == "clients") { $name = array_shift($paths); if (empty($name)) { $this->handle_base($method); } else { $this->handle_name($method, $name); }} else { // We only handle resources under "clients" header("HTTP/1.1 404 Not Found");}
У нас есть два возможных результата:
- Ресурс - это клиенты, и в этом случае мы возвращаем полный список
- Существует еще один идентификатор
Если есть ещё один идентификатор, мы предполагаем, что это имя клиента, и, опять же, перенаправляем его в другую функцию, в зависимости от method
. Мы используем оператор switch
, которого следует избегать в реальном приложении:
switch($method) { case "PUT": $this->create_contact($name); break; case "DELETE": $this->delete_contact($name); break; case "GET": $this->display_contact($name); break; default: header("HTTP/1.1 405 Method Not Allowed"); header("Allow: GET, PUT, DELETE"); break; }
Коды ответов
Коды ответа HTTP стандартизируют способ информирования клиента о результате его запроса.
Вы могли заметить, что пример приложения использует PHP header()
, передавая некоторые странные строки в качестве аргументов. Функция header() печатает HTTP headers
и гарантирует, что они отформатированы соответствующим образом. Заголовки должны быть первым в ответе, поэтому не стоит выводить что-либо ещё до того, как вы закончите с заголовками. Иногда ваш HTTP-сервер может быть настроен для добавления других заголовков в дополнение к тем, которые вы указали в коде.
Заголовки содержат все виды метаинформации; например, текстовую кодировку, используемую в body сообщения, или MIME-тип содержимого body. В этом случае мы явно указываем коды ответа HTTP. Коды ответа HTTP стандартизируют способ информирования клиента о результате его запроса. По умолчанию PHP возвращает код ответа 200
, что означает, что ответ выполнен успешно.
Сервер должен вернуть наиболее подходящий код ответа HTTP; таким образом клиент может попытаться исправить свои ошибки, если они есть. Большинство людей знакомы с распространенным кодом ответа 404 Not Found
, однако есть много более доступных, в соответствии множеству ситуаций.
Имейте в виду, что значение кода ответа HTTP не является чрезвычайно точным; это следствие того, что HTTP сам по себе довольно общий. Вы должны попытаться найти код ответа, который наиболее точно соответствует ситуации. Но и не слишком переживайте, если не сможете найти точное соответствие.
Вот несколько HTTP-кодов ответа, которые часто используются с REST:
200 OK
Этот код ответа указывает, что запрос был успешным.
201 Created
Это означает, что запрос был успешным и был создан ресурс. Он используется в случае успеха запроса PUT
или POST
.
400 Bad Request
Запрос был утерян. Это происходит особенно с запросами POST
и PUT
, когда данные не проходят валидацию или находятся в неправильном формате.
404 Not Found
Этот ответ указывает, что необходимый ресурс не найден. Обычно это относится ко всем запросам, которые указывают на URL-адрес без соответствующего ресурса.
401 Unauthorized
Эта ошибка означает, что вам необходимо выполнить проверку подлинности перед доступом к ресурсу.
405 Method Not Allowed
Используемый метод HTTP не поддерживается для этого ресурса.
409 Conflict
Это указывает на конфликт. Например, вы используете запрос PUT
для создания одного и того же ресурса дважды.
500 Internal Server Error
Когда всё остальное терпит неудачу; как правило, ответ 500 используется, когда обработка завершается неудачно из-за непредвиденных обстоятельств на стороне сервера, что вызывает ошибку сервера.
Выполнение образца приложения
Давайте начнем с простого извлечения информации из приложения. Нам нужны детали клиента, "jim
", поэтому давайте отправим простой запрос GET
на URL этого ресурса:
curl -v http://localhost:80/clients/jim
Это отобразит полные сообщения headers. Последней строкой ответа будет body сообщения; в этом случае это будет JSON, содержащий адрес Jim (помните, что пропуск имени метода приведет к GET
-запросу, а также замените localhost: 80
на имя сервера и порт, который вы используете).
Затем мы можем получить информацию для всех клиентов одновременно:
curl -v http://localhost:80/clients/
Чтобы создать нового клиента с именем Paul ...
curl -v -X PUT http://localhost:80/clients/paul -d "{"address":"Sunset Boulevard" }
и вы получите список всех клиентов, содержащих Paul в качестве подтверждения.
Наконец, чтобы удалить клиента:
curl -v -X DELETE http://localhost:80/clients/anne
Вы обнаружите, что возвращённый JSON больше не содержит никаких данных об Anne.
Если вы пытаетесь получить несуществующего клиента, например:
curl -v http://localhost:80/clients/jerry
Вы получите ошибку 404, в то время как при попытке создать уже существующего клиента:
curl -v -X PUT http://localhost:80/clients/anne
вместо этого получите ошибку 409.
Заключение
В общем, чем меньше предположений за пределами HTTP вы делаете, тем лучше.
Важно помнить, что HTTP был задуман для взаимодействия между системами, которые ничто не разделяет, кроме понимания протокола. В целом, чем меньше допущений за пределами HTTP вы делаете, тем лучше: это позволяет широкому кругу программ и устройств получать доступ к вашему API.
Я использовал PHP в этом уроке, потому что это, скорее всего, язык, наиболее знакомый читателям Nettuts +. Тем не менее, PHP, хотя и предназначен для Интернета, вероятно, не самый лучший язык для работы при REST-способе, поскольку он обрабатывает запросы PUT
совсем иначе, чем GET
и POST
.
Помимо PHP, вы можете принять во внимание следующее:
- Различные Ruby frameworks (Rails и Sinatra)
- В Python есть отличная поддержка REST. Должны работать Plain Django и WebOb, или Werkzeug.
- node.js отлично поддерживает REST
Среди приложений, которые пытаются придерживаться принципов REST, классическим примером является Atom Publishing Protocol, хотя на самом деле он не используется слишком часто на практике. За современным приложением, основанным на философии использования HTTP в полной мере, обратитесь к Apache CouchDB.
Удачи!