Архитектуру REST часто ошибочно путают с некой методикой именования URL на сайте и распределения функций между GET, POST, PUT и DELETE запросами, что в корне не верно. REST — это мощнейший архитектурный стиль, затрагивающий всё ваше приложение, концентрирующийся на производительности и совместимости с другими веб-системами.

Между веб-сервером, на котором работает веб-сайт, и браузером клиента находится громадное количество промежуточных узлов, которые обрабатывают и кэшируют передаваемую информацию. Кроме того, на сайт заходят поисковые роботы. Всем им безразличны архитектурные велосипеды — они воспринимают веб-сайт, как черный ящик с HTTP-интерфейсом. От того, на сколько точно веб-сервер следует рекомендациям протокола, зависит эффективность его работы.

Унифицированный программный интерфейс

Как-то мне пришел на поддержку проект с большим каталогом товаров. У каждого товара была своя страничка с фотографиями, описанием, отзывами и т.д. Вроде бы всё как обычно. Но клиент жаловался на то, что на почту в большом объеме льются письма с информацией о выставлении товарам оценок. И правда: на сайте у каждого товара было много оценок, большинство — средненькие. На что я обратил внимание: звездочки в голосовалке товаров были оформлены ссылками на php-скрипт, который запоминал оценку и высылал письмо клиенту с уведомлением. Поисковый робот заходил на страничку товара, видел набор ссылок и начинал переходить по этим ссылкам, чтобы их проиндексировать, таким образом запуская скрипт выставления оценки. К слову, он заходил по всем ссылкам со звездочек равномерно, поэтому и оценки получались средние.

Почему это происходило? Поисковый робот воспринимал GET-запросы, которые он делает, как безопасные и идемпотентные. Но обработчик этого GET-запроса был небезопасным и неидемпотентным.

Таблица соответствия HTTP-метода безопасности и идемпотентности
HTTP-Метод Безопасный Идемпотентный
GET Да Да
HEAD Да Да
OPTIONS Да Да
PUT Нет Да
DELETE Нет Да
POST Нет Нет

Безопасный запрос — это запрос, который не меняет состояние приложения.

Идемпотентный запрос — это запрос, эффект которого от многократного выполнения равен эффекту от однократного выполнения.

Так как POST-запросы небезопасные и неидемпотентные, браузер никогда не повторяет отправку формы POST-запросом автоматически.

Идемпотентный запрос можно повторять многократно, если нет уверенности, что предыдущий такой же запрос был выполнен. Например, если необходимо удалить книгу из библиотеки по определенному ISBN, используя для этого DELETE-запрос, можно будет послать повторный запрос на удаление. Результатом даже нескольких запросов будет удаление книги по данному ISBN — ожидаемый результат. Если необходимо поменять название у определенной книги PUT запросом, то без опасения можно послать несколько запросов. Результатом будет изменение названия книги на необходимое.

В то же время многократное обращение к функции добавления книги посредством POST-запроса приведет к добавлению нескольких книг. Именно поэтому повторные неидемпотентные запросы могут привести к неожиданным результатам.

Из принципов идемпотентности и безопасности следуют отношения между HTTP-методами и CRUD-операциями.

HTTP-метод Операция
POST Create
GET Read
PUT Update
DELETE Delete

Унификация интерфейса позволяет не делать типовых ошибок и не нарушать семантику HTTP-запросов.

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

Не храним состояние клиента

Еще один веб-сайт, реализованный на коммерческом движке интернет-магазина, хранил сессии в БД. Для генерирования каждой страницы он запускал сессии. В момент старта сессии он создавал запись в таблице БД, а когда заканчивал обрабатывать запрос клиента, сохранял сессионные данные. Сессии идентифицировал по кукам. Если приходил поисковый робот (а он обычно не запоминает куки), то каждая сгенерированная страничка порождала старт сессии и вставку в сессионную таблицу. Вместе с приличной посещаемостью веб-сайта это приводило к большой нагрузке на сессионную таблицу: постоянно шли запросы на создание сессии, на её обновление. А вставка больших сериализованных данных из сессии в БД занимала приличное количество времени и иногда подвешивала сайт.

А теперь вопрос: зачем стартовать сессию при прорисовке страницы, если большая часть информации на этой странице от пользователя не зависит? Не лучше было бы сгенерировать страницу вообще без пользовательских данных, не трогая сессии, а потом уже подгрузить оставшиеся элементы таким образом, чтобы сессия не запускалась до авторизации пользователя?

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

Кэширование

PHP-разработчики любят memcache. Когда возникают проблемы с производительностью, то предлагают закэшировать или тяжелые запросы к БД, или часть генерируемой страницы, или даже вообще весь контент страницы, если сессионные данные не мешаются. И это называется оптимизацией производительности.

Печальная правда состоит в том, что большинство проектов, которые начинают прикручивать memcache, могли бы этого не делать при их посещаемости, если бы следовали REST-архитектуре.

Протокол HTTP содержит все необходимые заголовки для организации кэширования как на стороне клиента, так и промежуточными звеньями между клиентом и веб-сервером. Эти заголовки позволят избегать лишних запросов к серверу, а также при необходимости размещать непосредственно перед веб-сервером специализированное ПО для кэширования (например, Squid), внося минимум дополнительной логики в само приложение.

Обычно кэширующие заголовки выставляют только для успешных запросов (200 OK), но также можно кэшировать и другие запросы, в том числе с ошибками:

Правильно разбивая веб-приложение на компоненты, организуя прозрачное кэширование на стороне клиента, можно добиться значительных результатов. Но прежде всего необходимо представлять, как работает протокол HTTP, чтобы использовать все его возможности и глубже понимать принципы построения RESTful-приложений.