Проектирование REST приложений
Одна из причин популярности веб-приложений заключается в возможности масштабирования нагрузки и разделение обязанностей между компонентами системы. Для управления сложностью веб-приложений используется клиент-серверная архитектура, которая позволяет не только разделять обязанности, но и значительно усложнять серверную составляющую, делая ее многоуровневой с большим количеством промежуточных звеньев.
Так как большую часть веб-приложения занимает не бизнес-составляющая, а инфраструктура, возникает задача управления над корректной передачей состояния бизнес-логики. Чтобы использовать все преимущества распределенных систем, был придуман архитектурный стиль REST, а его адаптация на веб-приложения стала называться RESTful. Говоря про REST-прилоежния чаще всего имеются в виду именно RESTful приложения.
В этом стриме мы рассмотрим основные архитектурные моменты, связанные с построением таких приложений. Предложенные принципы и правила позволят строить и расширять веб-приложения, не нарушая принципов REST и получая все преимущества таких систем.
Основы
Duration: 7
REST не является конкретным паттерном, а является архитектурным стилем, поэтому большое множество разных решений могут подходить под требования REST. Этого достаточно, чтобы получить все бонусы использования этого архитектурного стиля.
Ограничения следующие:
- Модель клиент сервер - принцип черного ящика у клиента и сервера;
- Отсутствие состояния - каждый запрос самодостаточен;
- Кэширование - идентификация ресурса должна позволять кэшировать результат когда это нужно;
- Единообразие интерфейсов
- Идентификация ресурсов;
- Манипуляция ресурсами через представление;
- «Самоописываемые» сообщения;
- Гипермедиа как средство изменения состояния приложения;
- Слои - разделение абстракций и обязанностей по слоям - сетевые системы являются многослойными, т.е. сервер может быть только прокси к другим серверам;
- Код по требованию - функциональность клиента может быть расширена через поставку кода с сервера.
Ресурсы
Duration: 10
- Все ресурсы - структуры
- URN развертывается в представление данных
- Ресурс с типом сущность неделим
- {...} - структура обозначает тип "сущность"
- [...] - структура обозначает тип "коллекция"
- (a,b) - структура обозначает "связь"
- косой чертой (/) - обозначается бинарное отношение между структурами разных типов
- "связь" имеет специальный смысл и всегда должна находиться в служебном подмножестве "связи"
- идентификаторы могут быть составными и результат получается путем свертки (слева направо) и применению правила развертывания в представление.
Идентификатор
Duration: 5
- Идентификатор - не отражает структуру хранения ресурсов
- Идентификатор не обязаны быть составным сотсавной:/users/:id/orders/:id несоставной: /userorders/:userid
- Идентификатор не определяет представление ресурса - /users/:id/orders и /books/:id/orders могут содержать одинаковые заказы, но мы не знаем получены они через ссылку или через копирование значения
- URL должен поддерживать простые правила расширения
Представление
Duration: 10
Ресурсы - это структура наполненная данными, количество данных исчерпывающе для описанного ресурса. Структура хранится целеком, но может быть отображена по-разному. Пользователь получает отображение ресурса, а не сам ресурс.
- Разработчик сам определяет представление ресурсов
- Варианты представления:
- неупорядоченное глобальное множество мапится на ID
- множество множеств находящихся в отношении "связь"
- множество вложенных множеств в отношении "содержит"
- множество комбинация все предыдущих способов
- любой другой способ
- Идемпотентность запросов
Получение данных, маппинг на HTTP
Duration: 5
REST приложения не обязаны основываться на HTTP протоколе, но практические реализации REST реализованы в веб-приложениях, работающих на HTTP. Поэтому на практике REST представлен RESTful.
HTTP запросы можно взаимооднозначно отобразить на функции: - GET /users/:id => getusersid(id) - GET /users/:id => get(usersid) - GET /users/:id?status=active => getuser_id(id, status = 'active')
Временные коллекции
Duration: 5
В предложенной реализации REST есть возможность объединять в рамках одного запроса несколько действий:
GET(/users/:id/posts/2022)
POST(/users/:id/posts, []) = 2022 # обновляется алиас, но предыдущие коллекции остаются по их URN
PUT(2022, GET(users/:id/posts, params: {filter: {year = 2022}}))
GET(/users/:id/posts/2022) # возвращает результат значит является представлением и должно примениться правило развертывания
Но мы можем сократить лишние действия и получить REST, который будет работать в рамках предложенных ограничений:
`
GET(/users/:id/posts/2022) => GET(users/:id/posts, params: {filter: {year = 2022}}))
`
временная коллекция, которую можно получить путем фильтрации
Коды ответов или ошибки в JSON
Duration: 7
- Бизнес ошибки
- Инфраструктурные ошибки