Обложка: Основы REST: теория и практика

Основы REST: теория и практика

REST, Representational State Transfer, является архитектурным стилем для обеспечения стандартов между компьютерными системами в сети, что облегчает для систем обмен данными друг с другом. Системы, отвечающие требованиям REST и часто называемые RESTful, характеризуются тем, что не имеют сохранения состояния и разделяют интересы клиента и сервера. Мы рассмотрим, что означают эти термины и почему они являются полезными для услуг в Интернете.

Разделение клиента и сервера

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

До тех пор, пока каждая сторона знает, какой формат сообщений следует направлять другой стороне, они могут храниться модульно и раздельно. Отделяя задачи пользовательского интерфейса от задач хранения данных, мы повышаем гибкость интерфейса между платформами и улучшаем расширяемость за счёт упрощения компонентов сервера. Кроме того, разделение позволяет каждому компоненту развиваться независимо.

Используя интерфейс REST, различные клиенты попадают в одни и те же конечные точки REST, выполняют те же действия и получают одинаковые ответы.

Отсутствие сохранения состояния

Системы, которые следуют парадигме REST, не имеют сохранения состояния, что означает, что серверу не нужно знать о состоянии клиента и наоборот. Таким образом, и сервер, и клиент могут понять любое полученное сообщение, даже не увидев предыдущих сообщений. Это отсутствие сохранения состояния обеспечивается за счёт использования ресурсов, а не команд. Они описывают любые объекты, документы или вещи, которые могут потребоваться для хранения или отправки в другие службы.

Эти ограничения помогают RESTful-приложениям достигать надёжности, быстрой производительности и расширяемости, как компонентам, которые могут быть управляемы, обновлены и повторно использованы, не затрагивая систему в целом даже во время её работы.

Теперь мы изучим, как на самом деле происходит взаимодействие между клиентом и сервером, когда мы внедряем RESTful-интерфейс.

Взаимодействие между клиентом и сервером

В архитектуре REST клиенты отправляют запросы на поиск или изменение ресурсов, а серверы отправляют ответы на эти запросы. Давайте рассмотрим стандартные способы направления запросов и ответов.

Отправка запросов

REST требует, чтобы клиент сделал запрос на сервер для получения или изменения данных на сервере. Запрос обычно состоит из:

  • НТТР-метода, который определяет вид операции;
  • заголовка, который позволяет клиенту передавать информацию о запросе;
  • пути к ресурсу;
  • необязательного тела сообщения, содержащего данные.

Существует 4 основных метода НТТР, которые мы используем в запросах для взаимодействия с ресурсами в системе REST:

  • GET — получение конкретного ресурса (по id) или коллекцию ресурсов;
  • POST — создание нового ресурса;
  • PUT — обновление конкретного ресурса (по id);
  • DELETE — удаление конкретного ресурса по id;

В заголовке запроса клиент отправляет тип контента, который он может получить с сервера. Это поле называется Accept. Оно обеспечивает, что сервер не посылает данные, которые не могут быть поняты или обработаны клиентом. Параметры типов контента — это типы MIME (или Multipurpose Internet Mail Extensions, о которых вы можете прочитать больше в MDN Web Docs).

Типы MIME, используемые для указания типов контента в поле Accept, состоят из типа и подтипа. Они разделены слэшем (/).

Например, текстовый файл, содержащий HTML, будет указан с типом text/html. Если этот текстовый файл содержит CSS, то он будет указан как text/css. Общий текстовый файл будет обозначаться как text/plain. Однако это значение по умолчанию, text/plain, не является исчерпывающим. Если клиент ожидает text/css, а получает text/plain, он не сможет распознать содержание.

Другие типы и часто используемые подтипы:

  • image — image/png, image/jpeg, image/gif;
  • audio — audio/wav, audio/mpeg;
  • video — video/mp4, video/ogg;
  • application — application/json, application/pdf, application/xml, application/octet-stream.

Например, клиент, имеющий доступ к ресурсу с идентификатором 123 в ресурсе статей на сервере, может отправить запрос GET следующим образом:

GET/news/123
Accept: text/html, application/xhtml

В этом случае поле заголовка Accept говорит, что клиент примет содержание в text/html или application/xhtml.

Запросы должны содержать путь к ресурсу, на котором должна выполняться операция. В RESTful API пути должны быть разработаны так, чтобы помочь клиенту понять, что происходит. Обычно первая часть пути должна быть множественной формой ресурса. Это позволяет легко читать и понимать вложенные пути.

Пути должны содержать информацию, необходимую для определения местоположения ресурса с необходимой степенью конкретности. При ссылке на список или коллекцию ресурсов не всегда необходимо добавлять идентификатор. Например, запрос POST на путь somesite.com/persons не будет нуждаться в дополнительном идентификаторе, так как сервер генерирует идентификатор для нового объекта.

В тех случаях, когда сервер отправляет клиенту полезную нагрузку, он должен включать тип контента в заголовок ответа. Это поле заголовка контента предупреждает клиента о типе данных, которые он посылает в теле ответа. Эти типы контента являются типами MIME, точно так же, как они находятся в поле Accept заголовка запроса. Тип контента, который сервер возвращает обратно в ответе, должен быть одним из параметров, указанных клиентом в поле принятия запроса.

Например, клиент получает доступ к ресурсу с идентификатором 123 в разделе статей с этим запросом GET:

GET /news/123 HTTP/1.1
Accept: text/html, application/xhtml

Сервер должен отправить обратно контент с заголовком ответа:

HTTP/1.1 200 (OK)
Content-Type: text/html

Это означает, что запрашиваемый контент возвращается в тело ответа с text/html — типом контента, который клиент будет в состоянии принять.

Коды ответов

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

Для каждого метода НТТР ожидаются коды статуса, которые сервер должен вернуть в случае успеха:

  • GET — return 200 (OK)
  • POST — return 201 (CREATED)
  • PUT — return 200 (OK)
  • DELETE — return 204 (NO CONTENT)

Если операция не работает, вернётся наиболее конкретный код состояния, соответствующий возникшей проблеме.

Предположим, у нас есть приложение, которое позволяет вам просматривать, создавать, редактировать и удалять клиентов и заказы для небольшого магазина одежды, размещённого на сайте fashionboutique.com. Мы можем создать НТТР API, который позволит клиенту выполнять следующие функции.

Если бы мы хотели увидеть всех клиентов, запрос выглядел бы так:

GET http://somesite.com/persons
Accept: application/json

Возможный заголовок ответа будет выглядеть следующим образом:

Status Code: 200 (OK)
Content-type: application/json

Затем следуют данные клиентов, запрошенные в формате application/json.

Создание нового клиента путем размещения данных:

POST http://somesite.com/persons

Body:
{
  “person”: {
    “name” = “oleg Melnic”,
    “email” = “oleg.melnic@gmail.com”
  }
}

Затем сервер генерирует идентификатор этого объекта и возвращает его клиенту с таким заголовком:

201 (CREATED)
Content-type: application/json

Для просмотра одного клиента мы используем метод GET, указывая идентификатор этого клиента:

GET http://somesite.com/persons/123
Accept: application/json

Возможный заголовок ответа будет выглядеть следующим образом:

Status Code: 200 (OK)
Content-type: application/json

Затем следуют данные ресурса клиента с идентификатором 123 в формате application/json.

Мы можем обновить этого клиента, вставив новые данные с помощью метода PUT:

PUT http://somesite.com/persons/123

Body:
{
  “person”: {
    “name” = “Oleg Melnic”,
    “email” = “olegmelnic1@gmail.com”
  }
}

Возможный заголовок ответа будет иметь Status Code: 200 (OK), чтобы сообщить клиенту, что элемент с идентификатором 123 был изменен.

Мы также можем УДАЛИТЬ этого клиента, указав его идентификатор:

DELETE http://somesite.com/persons/123

Ответ будет иметь заголовок, содержащий Status Code: 204 (NO CONTENT), уведомляющий клиента о том, что объект с идентификатором 123 был удалён и ничего в теле не осталось.

Практика с REST

Давайте представим, что мы создаём сайт для сбора фотографий. Мы хотим сделать API, чтобы отслеживать пользователей, места проведения и фотографии этих мест. Этот сайт имеет index.html и style.css. Каждый пользователь имеет имя пользователя и пароль. Каждая фотография имеет место проведения и владельца (т.е. пользователя, который сделал фотографию). Каждое место имеет название и адрес. Можете ли вы разработать систему REST, которая будет учитывать:

  • хранение пользователей, фотографий и мест проведения;
  • доступ к определённым местам и доступ к некоторым фотографиям определённого места проведения?

Для начала опишите:

  • какие запросы мы хотели бы сделать;
  • какие ответы должен вернуть сервер;
  • каким должно быть содержание каждого ответа.

Возможные решения — модели

{
  “user”: {
    "id": <Integer>,
    “username”: <String>,
    “password”:  <String>
  }
}
{
  “photo”: {
    "id": <Integer>,
    “address_id”: <Integer>,
    “user_id”: <Integer>
  }
}
{
  “address”: {
    "id": <Integer>,
    “name”: <String>,
    “value”: <String>
  }
}

Возможное решение — запросы / ответы

GET-запросы

Request

GET /index.html
Accept: text/html

Response

200 (OK)
Content-type: text/html

Request

GET /style.css
Accept: text/css

Response

200 (OK)
Content-type: text/css

Request

GET /addresses
Accept: application/json

Response

200 (OK)
Content-type: application/json

Request

GET /addresses/:id
Accept: application/json

Response

200 (OK)
Content-type: application/json

Request

GET /addresses/:id /photos/:id
Accept: application/json

Response

200 (OK)
Content-type: image/png

POST-запросы

Request

POST /users

Response

201 (CREATED)
Content-type: application/json

Request

POST /addresses

Response

201 (CREATED)
Content-type: application/json

Request

POST /addresses/:id /photos

Response

201 (CREATED)
Content-type: application/json

PUT-запросы

Request

PUT /users/:id

Response

200 (OK)

Request

PUT /addresses/:id

Response

200 (OK)

Request

PUT /addresses/:id /photos/:id

Response

200 (OK)

DELETE-запросы

Request

DELETE /addresses/:id

Response

204 (NO CONTENT)

Request

DELETE /addresses/:id/ photos/:id

Response

204 (NO CONTENT)

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