[Из песочницы] Создание RESTful сервисов на Meteor
Введение: Зачем нужен RESTful сервис на Meteor
Meteor привлекает простотой использования и возможностью очень быстро создать работающее приложение с минимальным набором функций. У Meteor — хорошо развитое сообщество. Есть множество полезных дополнительных модулей, которые не требуют сложной настройки, и могут быть использованы сразу после установки. Есть хорошая документация, примеры и большое количество постов на форумах, вроде StackOverflow. Meteor — это full-stack фреймворк, который предлагает удобную и многофункциональную интеграцию сервера с клиентом. Так зачем же выходить за рамки этого взаимодействия, и создавать RESTful сервис?
Клиент-серверное приложение, по-сути, состоит из 2 независимых частей, которые которые взаимодействуют посредством определенного интерфейса. При этом каждая из частей клиент-серверного приложения может создаваться разными людьми или командами. Разработчики клиенсткой части вовсе не ограничены использованием Meteor, они могут использовать любой другой JS фреймворк, клиент даже не обязательно должен быть написан на JS, это может быть к примеру приложение Android, написанное на Java, или iOS, написанное на Objective C.
Именно эти причины заставили меня выбрать Meteor для построения back end в моем проекте, и искать пути для создания RESTful сервиса на Meteor.
Обзор имеющихся модулей
После некоторого времени, потраченного на поиск подходящий модулей, у меня появился следующий список:
github.com/meteorhacks/picker — использует маршрутизацию аналогичную той, что используется, скажем, в Express.js
Picker.route('/post/:_id', function(params, req, res, next) {
var post = Posts.findOne(params._id);
res.end(post.content);
});
с возможностью отправки ответа в формате JSON
github.com/crazytoad/meteor-collectionapi позволяет создать API Endpoints для CRUD операций над коллекциями. Нет механизмов для разграничения уровней доступа (к примеру: гость, авторизованный пользователь, администратор), авторизации или создания custom endpoints.
github.com/kahmali/meteor-restivus позволяет создать API Endpoints для CRUD операций над коллекциями. Имеются механизмы авторизации, разграничения уровней доступа и создания custom endpoints. Об этом пакете будет рассказано подробней чуть ниже.
github.com/Differential/reststop2 — устаравшее и неподдерживаемое решение. На главной странице ресурса — ссылка на проект Restivus с указанием о том, что все имеющиеся функции этого решения также есть и в Restivus.
github.com/stubailo/meteor-rest позволяет создать API Endpoints для CRUD операций над коллекциями. Имеются механизмы авторизации и создания custom endpoints. Нет механизмов для разграничения уровней доступа. Документации не хватает ясности и работающих примеров. Позволяет интегрировать Restivus для создания custom endpoints.
Поскольку Restivus имеет весь необходимый мне набор функций, и был отмечен наибольшим количеством звезд как на GitHub, так и на Atmosphere, а также по причине того, что 2 других проекта из приведенного списка ссылались на Restivus, я решил остановить свой выбор именно на нем.
Использование коллекций, CRUD операции и уровни доступа
Использовать Restivus — очень просто. Чтобы установить, введите в консоли:
meteor add nimble:restivus
Чтобы создать RESTful сервис, добавьте в серверный код следующие сроки:
if (Meteor.isServer) {
var Api = new Restivus({useDefaultAuth: true});
}
В аргументах конструктора передаются опции API. Значение опции `useDefaultAuth` рассмотрим в слеудющем разделе.
Если у вас зарегистрирована коллекция, скажем
var Contacts = new Mongo.Collection('contacts');
И вы хотите открыть к ней доступ посредством CRUD операций через API, в простейшей форме достаточно сделать:
Api.addCollection(Contacts);
Это создаст следующие Endpoints:
`getAll` Endpoint
GET /api/collection
Вернуть информацию обо всех элементах коллекции
`post` Endpoint
POST /api/collection
Добавить новый элемент в коллекцию
`get` Endpoint
GET /api/collection/: id
Вернуть информацию об элементе коллекции
`put` Endpoint
PUT /api/collection/: id
Изменить элемент коллекции
`delete` Endpoint
DELETE /api/collection/: id
Удалить элемент из коллекции
Формат ответа всегда следующий:
{status: "success", data: {}}
Префикс `api/` может быть заменен на другой посредством установки опции `apiPath`, переданной при создании API. Как вы видите, у каждой API операции над коллекцией есть свой идентификатор. Он может быть использован следующим образом:
Api.addCollection(Contacts, {
excludedEndpoints: ['getAll', 'put'],
routeOptions: {
authRequired: true
},
endpoints: {
get: {
authRequired: false
},
delete: {
roleRequired: 'admin'
}
}
});
В этом случае в API была зарегистрирована коллекция с операциями delete, get и post. Для операции get авторизация не требуется, для операций post и delete — требуется, при этом для операцию delete может выполнить только администратор. Об авторизации и аутентификации читайте далее
Авторизация и аутентификация
После регистрации в API коллекции users добавляются 2 специальные endpoints:
POST /api/login
GET|POST /api/logout
Несмотря на заверения из документации, в версии Restivus, которую использовал я (0.8.4), разлогивание работало только при GET запросе. Более подробно об этом можете посмотреть здесь.
Чтобы авторизоваться, нужно передать имя пользователя и пароль в следующем виде:
curl http://localhost:3000/api/login/ -d "username=test&password=password"
В случае неудачи в теле ответа прийдет строка 'Unauthorized'.
В случае успеха прийдет следующий ответ:
{status: "success", data: {authToken: "f2KpRW7KeN9aPmjSZ", userId: fbdpsNf4oHiX79vMJ}}
Сохраните токен и id пользователя, чтобы передавать в заголовках запросов к API Endpoints, требующих авторизации:
curl -H "X-Auth-Token: f2KpRW7KeN9aPmjSZ" -H "X-User-Id: fbdpsNf4oHiX79vMJ" http://localhost:3000/api/contacts/
Custom Endpoints
Api.addRoute('contacts/favorite/:userId', {
get: {
authRequired: false,
roleRequired: ['author', 'admin'],
action: function () {
this.response.write({"user-id": this.urlParams.userId});
this.done();
}
}
});
Этот фрагмент кода регистрирует API Endpoint по адресу api/contacts/favorite/. Параметр `userId`, переданный как часть пути, будет доступен как `this.urlParams.userId`. Параметры GET запроса будут доступны через `queryParams`. Если не требуется описывать опции для авторизации и уровней доступа, то можно использовать краткую форму:
Api.addRoute('contacts/favorite/:userId', {
get: function () {
this.response.write({"user-id": this.urlParams.userId});
this.done();
}
});
Итоги: За и против построения RESTful сервиса на Meteor
Как мы видим, Meteor в очередной раз предоставил удобный инструмент для быстрого создания прототипа, что является очень ценным на ранних стадиях проекта. Нередко приложения не выходят за рамки работы с тектовыми и числовыми данными. Таким образом мы имеем: Meter и Restivus + resource механизм (имеющийся, к примеру в Angular или Vue) = взаимодействие между клиентом и сервером, созданное за считанные минуты.