Так ли нужен REST для веб-приложений?
Что такое REST?
Термин REST был введен в 2000 году Роем Филдингом. Системы которые базируются на REST принято называть RESTful. Сам по себе REST это не какой-то стандарт, или применяемая технология, это архитектурный стиль. Он лишь дает вам методологию, а реализацию оставляет на ваше усмотрение.
REST предлагает брать в качестве уникального идентификатора для единицы ресурса (к примеру товар или пользователь) url адрес, а определять действие с этой единицей - по типу запроса.
Обычное среднестатистическое веб-приложение обходится двумя типами запросов: GET и POST, для получения и соответственно отправки данных. Но RESTful архитектура подразумевает использовать весь спектр запросов: GET - получить данные, POST - отправить новые данные, PUT - отправить отредактированные данные, DELETE - удалить данные. Есть ещё PATCH, который по своей идеи повторяет PUT, различия лишь в том, что PUT предполагает заменить полностью запись, а PATCH только частично обновить данные. Что интересно, мнения об использовании POST и PUT расходятся. Англоязычные разработчики предпочитают использовать именно PUT для добавления новых данных, а POST для редактирования.
Использование REST на примере GML
Для начала нам нужно определиться с ресурсами, с которыми может взаимодействовать пользователь. На GML у меня получилось: линки (ссылки на веб-ресурсы), сборники (контейнеры для линков), пользователи и теги. Это четыре основных единицы, с которыми происходит работа.
Обозначим корневой домен как gml.link/
У нас получаются следующие запросы на получение данных:
- GET gml.link/links/ - получить весь список линков
- GET gml.link/links/125 - получить сто двадцать пятый линк
- GET gml.link/collections/ - получить список всех сборников
- GET gml.link/collection/34 - получить список линков тридцать четвертого сборника
- GET gml.link/tags/ - получить список всех тегов
- GET gml.link/tags/animal - получить список линков с тегом 'animal' (для тегов в качестве идентификатора выступает его название, а не номер.)
Запросы на добавление данных (в круглых скобках указаны данные передаваемые в теле запроса)
- POST gml.link/users/ (username: 'Alex', password: 'sdf7665$6', email: 'alex@mail.ru') - добавить нового пользователя
- POST gml.link/users/23 (username: 'Alex', password: 'sdf7665$6', email: 'alex@mail.ru') - в данном случае вернется ошибка, так как мы указали в запросе идентификатор конкретного пользователя
- POST gml.link/collections/ (title: 'Новая коллекция', 'type': 'private') - создаст новый приватный сборник
Запросы на редактирование
- PUT gml.link/collections/ (title: 'Измененное название сборника') - вернет ошибку, так как не указан идентификатор сборника
- PUT gml.link/collections/12 (title: 'Измененное название сборника') - изменит название у двенадцатого сборника
- PUT gml.link/links/117 (title: 'Другое название линка') - изменит название у сто семнадцатого линка
Запросы на удаление
- DELETE gml.link/collection/32 - удалит тридцать второй сборник
- DELETE gml.link/links/43 - удалит сорок третий линк
Как мы видим запросы для пользователя получаются совершенно прозрачными, без тайных знаков и выражений. К примеру в предыдущей версии сервиса GML получение списка линков выглядело следующим образом: POST http://gml.link/action.php с кучей параметров в теле запроса типа action=get_links, start=0, finish=30 и так далее, причем в адресной строке был адрес http://gml.link/index.php?target=category&id=l4. В принципе адрес можно было и не менять, так как запрос происходит через Ajax, но он был нужен для сохранения состояния, чтоб при обновлении страницы (F5) не сбрасывалась открытая категория. И это было ужасно.
Подозреваю что многие уже задались вопросом, как же правильно передавать дополнительные параметры запроса для получения данных. REST архитектура не запрещает в GET запросах передавать дополнительные данные. Это даже необходимо в некоторых случаях. К примеру на сервисе GML реализована так называемая "ленивая загрузка" (lazy load), когда данные подгружаются по мере надобности (в моем случае при скролле страницы). Поэтому на запрос линков нужно передавать ещё и страницу (если быть точнее с какого линка и сколько выбрать), поэтому запрос на получение данных с дополнительными параметрами выглядит так: GET gml.link/links/?page=3. Так же дополнительные данные нам понадобятся для поиска: GET gml.link/links/?search=javascript
Совсем немного о технической реализации
(внимание, прочитав данный параграф, ваша жизнь может навсегда измениться)
Так как мы не на хабрахабре, поэтому техническую реализацию я затрону условно.
Для многих, кто только начал знакомиться с архитектурой RESTful, возникает вопрос, кто обработает запрос вида GET gml.link/collections/45, ведь по старой школе запрос идет по пути public_html/collections/45/index.html (public_html - корень сайта). Но у нас нет такой структуры папок, и вообще только один index файл. На первый взгляд может показаться что это очередные проделки лукавого, но нет, во всем виноваты хитрые люди, которые придумали RewriteEngine (механизм преобразований, управляется сервером Apache, конфигурируется в файле .htaccess), чтоб дурить нашего брата. На самом же деле наш запрос идет по адресу gml.link/index.php?target=collection&id=45, просто RewriteEngine незаметно для пользователя подменяет все запросы и перенаправляет в нужное русло. Составив нужные нам правила для замены адресов, мы держим строку адреса в чистоте и здравом смысле. А в остальном (PUT, DELETE запросы) все по честному.
Так как приложение у нас одностраничное, и все запросы передаются посредством ajax, то для смены url адреса в адресной строке, нужно вручную запоминать состояние страницы через HTML5 History API, которое мы не будем рассматривать в этом посте.
Весь процесс обработки запроса делится на несколько этапов:
- Определение ресурса, к которому направлен запрос (links, collections, users и tags, и id ресурса)
- Определение действия из типа запроса (получение, добавление, редактирование и удаление)
- Сбор дополнительных параметров из запроса (для добавления\редактирования - это непосредственно данные ресурса, для получения - это разные фильтры)
В чем преимущества данного архитектурного подхода?
Применив REST на своем проекте, для себя я отметил следующее:
- Понятные url адреса для пользователя
- Сохранение состояния страницы через HTML5 History API (как бонус)
- Нет путаницы с дополнительной передачей параметров в запросах и обработкой их на сервере
- Прозрачная логика клиентской части приложения
- В случае надобности легко предоставить RESTful API для сторонних приложений
Что дальше? (в общем)
Как я уже говорил, статьи буду писать по мере реализации той или иной части сервиса. На данный момент уже готова регистрация/аутентификация, полностью поддержана REST архитектура, реализована "lazy load" подгрузка линков, добавление/создание сборника.
Вы можете предложить тему для следующей статьи в рамках сервиса GML. Если же предложений не будет, то следующая статья будет о сборниках, их типах, для чего они нужны и как реализовано добавление/создание сборника.