Перейти к содержимому

Как подключить rest api к сайту js

  • автор:

Как подключить rest api к сайту js

Используя Fetch API в JavaScript, можно реализовать полноценный клиент для Web API в стиле REST для взаимодействия с пользователем. Архитектура REST предполагает применение следующих методов или типов запросов HTTP для взаимодействия с сервером:

Рассмотрим, как создать свой клиент на javascript для API.

Создание сервера на node.js

Для начала определим сервер, который будет и будет собственно представлять Web API. В качестве примера возьмем Node.js. Для обработки запросов определим следующий файл server.js :

const http = require("http"); const fs = require("fs"); // данные, с которыми работает клиент const users = [ < id:1, name:"Tom", age:24>, < id:2, name:"Bob", age:27>, < id:3, name:"Alice", age:"23">] // обрабатываем полученные от клиента данные function getReqData(req) < return new Promise(async (resolve, reject) => < try < const buffers = []; for await (const chunk of req) < buffers.push(chunk); >const data = JSON.parse(Buffer.concat(buffers).toString()); resolve(data); > catch (error) < reject(error); >>); > http.createServer(async (request, response) => < // получение всех пользователей if (request.url === "/api/users" && request.method === "GET") < response.end(JSON.stringify(users)); >// получение одного пользователя по id else if (request.url.match(/\/api\/users\/([0-9]+)/) && request.method === "GET") < // получаем id из адреса url const // получаем пользователя по id const user = users.find((u) =>u.id === parseInt(id)); // если пользователь найден, отправляем его if(user) response.end(JSON.stringify(user)); // если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > > // удаление пользователя по id else if (request.url.match(/\/api\/users\/([0-9]+)/) && request.method === "DELETE") < // получаем id из адреса url const // получаем индекс пользователя по id const userIndex = users.findIndex((u) =>u.id === parseInt(id)); // если пользователь найден, удаляем его из массива и отправляем клиенту if(userIndex > -1) < const user = users.splice(userIndex, 1)[0]; response.end(JSON.stringify(user)); >// если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > > // добавление пользователя else if (request.url === "/api/users" && request.method === "POST") < try< // получаем данные пользователя const userData = await getReqData(request); // создаем нового пользователя const user = ; // находим максимальный id const u.id;>)) // увеличиваем его на единицу user.id = id + 1; // добавляем пользователя в массив users.push(user); response.end(JSON.stringify(user)); > catch(error)< response.writeHead(400, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Некорректный запрос" >)); > > // изменение пользователя else if (request.url === "/api/users" && request.method === "PUT") < try< const userData = await getReqData(request); // получаем пользователя по id const user = users.find((u) =>u.id === parseInt(userData.id)); // если пользователь найден, изменяем его данные и отправляем обратно клиенту if(user) < user.age = userData.age; user.name = userData.name; response.end(JSON.stringify(user)); >// если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > > catch(error)< response.writeHead(400, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Некорректный запрос" >)); > > else if (request.url === "/" || request.url === "/index.html") < fs.readFile("index.html", (error, data) =>response.end(data)); > else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Ресурс не найден" >)); > >).listen(3000, ()=>console.log("Сервер запущен по адресу http://localhost:3000"));

Разберем в общих чертах этот код. Вначале идет определение данных, с которыми будет работать клиент:

const users = [ < id:1, name:"Tom", age:24>, < id:2, name:"Bob", age:27>, < id:3, name:"Alice", age:"23">]

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

Далее определена функция getReqData() , которая извлекает из запроса присланные от клиента данные и конвертирует их в формат json (предполагается, что клиент будет присылать данные в формате json):

function getReqData(req) < return new Promise(async (resolve, reject) => < try < const buffers = []; for await (const chunk of req) < buffers.push(chunk); >const data = JSON.parse(Buffer.concat(buffers).toString()); resolve(data); > catch (error) < reject(error); >>); >

Причем результат функции определен в виде промиса. Если данные успешно распарсены, то передаем через промис распарсенный объект. Если же произошла ошибка, то передаем сообщение об ошибке.

Далее для каждого типа запросов определен определенный сценарий.

Когда приложение получает запрос типа GET по адресу «api/users», то срабатывает следующий код:

if (request.url === «/api/users» && request.method === «GET»)

Здесь просто отправляем выше определенный массив users.

Когда клиент обращается к приложению для получения одного объекта по id в запрос типа GET по адресу «api/users/», то срабатывает следующий код:

else if (request.url.match(/\/api\/users\/([0-9]+)/) && request.method === "GET") < // получаем id из адреса url const получаем пользователя по id const user = users.find((u) =>u.id === parseInt(id)); // если пользователь найден, отправляем его if(user) response.end(JSON.stringify(user)); // если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > >

В этом случае нам надо найти нужного пользователя по id в массиве, а если он не был найден, возвратить статусный код 404 с некоторым сообщением в формате JSON.

При получении DELETE-запроса по адресу «/api/users/:id» находим индекс объекта в массива. И если объект найден, то удаляем его из массива и отправляем клиенту:

// удаление пользователя по id else if (request.url.match(/\/api\/users\/([0-9]+)/) && request.method === "DELETE") < // получаем id из адреса url const получаем индекс пользователя по id const userIndex = users.findIndex((u) =>u.id === parseInt(id)); // если пользователь найден, удаляем его из массива и отправляем клиенту if(userIndex > -1) < const user = users.splice(userIndex, 1)[0]; response.end(JSON.stringify(user)); >// если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > >

Если объект не найден, возвращаем статусный код 404.

При получении запроса с методом POST по адресу «/api/users» используем функцию getReqData() для извлечения данных из запроса:

else if (request.url === "/api/users" && request.method === "POST") < try< // получаем данные пользователя const userData = await getReqData(request); // создаем нового пользователя const user = ; // находим максимальный id const u.id;>)) // увеличиваем его на единицу user.id = id + 1; // добавляем пользователя в массив users.push(user); response.end(JSON.stringify(user)); > catch(error)< response.writeHead(400, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Некорректный запрос" >)); > >

Поскольку при выполнении функции промис может передавать ошибку (например, в результате парсинга в JSON), оборачиваем весь код в try..catch . После получения данных нам надо создать новый объект и добавить его в массив объектов.

Если приложению приходит PUT-запрос, то также с помощью функции getReqData() получаем отправленные клиентом данные. Если объект найден в массиве, то изменяем его, иначе отправляем статусный код 404:

// изменение пользователя else if (request.url === "/api/users" && request.method === "PUT") < try< const userData = await getReqData(request); // получаем пользователя по id const user = users.find((u) =>u.id === parseInt(userData.id)); // если пользователь найден, изменяем его данные и отправляем обратно клиенту if(user) < user.age = userData.age; user.name = userData.name; response.end(JSON.stringify(user)); >// если не найден, отправляем статусный код и сообщение об ошибке else< response.writeHead(404, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Пользователь не найден" >)); > > catch(error)< response.writeHead(400, < "Content-Type": "application/json" >); response.end(JSON.stringify(< message: "Некорректный запрос" >)); > >

Таким образом, мы определили простейший API. Теперь добавим код клиента.

Определение клиента

При обращении к корню веб-приложению или по адресу «/index.html», сервер будет отдавать файл index.html. Поэтому в одной папке с файлом сервера определим файл index.html со следующим кодом:

     Список пользователей  

Список пользователей

Сбросить
IdИмявозраст

Основная логика здесь заключена в коде javascript. При загрузке страницы в браузере получаем все объекты из БД с помощью функции getUsers:

async function getUsers() < // отправляет запрос и получаем ответ const response = await fetch("/api/users", < method: "GET", headers: < "Accept": "application/json" >>); // если запрос прошел нормально if (response.ok === true) < // получаем данные const users = await response.json(); let rows = document.querySelector("tbody"); users.forEach(user =>< // добавляем полученные элементы в таблицу rows.append(row(user)); >); > >

Для добавления строк в таблицу используется функция row() , которая возвращает строку. В этой строке будут определены ссылки для изменения и удаления пользователя.

Ссылка для изменения пользователя с помощью функции getUser() получает с сервера выделенного пользователя:

async function getUser(id) < const response = await fetch("/api/users/" + id, < method: "GET", headers: < "Accept": "application/json" >>); if (response.ok === true) < const user = await response.json(); const form = document.forms["userForm"]; form.elements["id"].value = user.id; form.elements["name"].value = user.name; form.elements["age"].value = user.age; >>

И выделенный пользователь добавляется в форму над таблицей. Эта же форма применяется и для добавления объекта. С помощью скрытого поля, которое хранит id пользователя, мы можем узнать, какое действие выполняется — добавление или редактирование. Если id равен 0, то выполняется функция createUser, которая отправляет данные в POST-запросе:

async function createUser(userName, userAge) < const response = await fetch("api/users", < method: "POST", headers: < "Accept": "application/json", "Content-Type": "application/json" >, body: JSON.stringify(< name: userName, age: parseInt(userAge, 10) >) >); if (response.ok === true) < const user = await response.json(); reset(); document.querySelector("tbody").append(row(user)); >>

Если же ранее пользователь был загружен на форму, и в скрытом поле сохранился его id, то выполняется функция editUser, которая отправляет PUT-запрос:

async function editUser(userId, userName, userAge) < const response = await fetch("api/users", < method: "PUT", headers: < "Accept": "application/json", "Content-Type": "application/json" >, body: JSON.stringify(< id: userId, name: userName, age: parseInt(userAge, 10) >) >); if (response.ok === true) < const user = await response.json(); reset(); document.querySelector("tr[data-rowid='" + user.id + "']").replaceWith(row(user)); >>

В конце запустим файл сервера server.js командой:

Реализация безопасного REST API в Node.js

Интерфейсы прикладного программирования или API (Application Programming Interface) применяются в разработке повсеместно. Они позволяют одним программам последовательно взаимодействовать с другими — внутренними или внешними — программными компонентами. Это является ключевым условием масштабируемости, не говоря уже о возможности повторного использования приложений.

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

Применяемый при этом стандарт де-факто называется «передачей состояния представления» (REpresentational State Transfer) или сокращённо REST. Простыми словами, REST API — это набор правил, по которым серверные приложения взаимодействуют с клиентскими.

Для создания простого, но безопасного бэкенда на основе REST API может быть задействовано множество платформ и языков программирования, например ASP.NET Core, Laravel (PHP) или Bottle (Python).

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

  • js — как пример распространённой кроссплатформенной среды выполнения JavaScript.
  • Express, который значительно упрощает выполнение основных задач веб-сервера в Node.js и является стандартным инструментом для создания серверной части на основе REST API.
  • Mongoose, который будет соединять наш бэкенд с базой данных MongoDB.

Помимо вышеперечисленного, пользователям данного руководства необходимо уверенно владеть навыками работы с терминалом (или командной строкой).

Обратите внимание! В рамках инструкции не будет рассмотрена работа с кодовой базой фронтенда. Тем не менее, можно легко использовать код (например, объектные модели на стороне сервера и клиента), так как наш бэкенд написан на JavaScript.

Архитектура REST

Чтобы понять, как работает REST API, нужно подробнее рассмотреть, что представляет собой стиль архитектуры программного обеспечения REST.

REST API используются для доступа к данными и их обработки с помощью стандартного набора операций без сохранения состояния. Эти операции являются неотъемлемой частью протокола HTTP. Они представляют собой основные функции создания («create»), чтения («read»), модификации («update») и удаления («delete») и обозначаются акронимом CRUD.

Операциям REST API соответствуют, хотя и не полностью идентичны, следующие методы HTTP:

  • POST (создание ресурса или предоставление данных в целом).
  • GET (получение индекса ресурсов или отдельного ресурса).
  • PUT (создание и замена ресурса).
  • PATCH (обновление/изменение ресурса).
  • DELETE (удаление ресурса).

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

Та же основа будет применяться для интеграции сторонних функций, о которых было сказано чуть выше. Большинство из них тоже использует REST API, что ускоряет такую интеграцию.

Исходные данные

В этом руководстве мы создадим самый простой REST API с нуля для ресурса под названием «users» с помощью Node.js.

У ресурса «users» будет следующая базовая структура:

  • id (UUID, что значит «автоматически сгенерированный универсальный уникальный идентификатор»).
  • firstName (Имя)
  • lastName (Фамилия)
  • email (Электронная почта)
  • password (Пароль)
  • permissionLevel (что разрешено делать данному пользователю).

Для этого ресурса будут созданы следующие операции:

  • «POST» в конечной точке «/users» (создание нового пользователя).
  • «GET» в конечной точке «/users» (вывод списка всех пользователей).
  • «GET» в конечной точке «/users/:userId» (получение конкретного пользователя).
  • «PATCH» в конечной точке «/users/:userId» (обновление данных для конкретного пользователя).
  • «DELETE» в конечной точке «/users/:userId» (удаление конкретного пользователя).

Кроме того, будет использоваться стандарт «JSON web token» для создания токенов доступа.

Поэтому мы создадим ещё один ресурс под названием «auth», который будет принимать адрес электронной почты и пароль пользователя. А в ответ — генерировать токен для аутентификации при определённых операциях.

Настройка бэкенда REST API

Прежде всего убедитесь, что у вас установлена самая последняя версия Node.js. Здесь будем использовать версию 14.9.0, однако могут подойти и более старые.

Затем позаботьтесь о том, чтобы у вас была установлена MongoDB. Не будем подробно расписывать особенности используемых здесь Mongoose и MongoDB.

Просто запустите сервер в интерактивном режиме (введя в командной строке « mongo »), а не как сервис. Дело в том, что в какой-то момент нам нужно будет взаимодействовать с MongoDB напрямую, а не через код Node.js.

Обратите внимание! С MongoDB нет необходимости создавать конкретную базу данных, как это бывает в сценариях некоторых реляционных СУБД. Первый вызов « insert » из кода Node.js инициирует её создание автоматически.

Данная статья не содержит всего кода, необходимого для рабочего проекта. Предполагается, что вы клонируете дополнительный репозиторий и просто следуете за основными пунктами, по мере чтения этого руководства. Если хотите, можете при необходимости копировать конкретные файлы и фрагменты кода из этого репозитория.

Перейдите к появившейся папке «rest-api-tutorial/» в терминале. Здесь находятся три папки модулей проекта:

  • «common» — все общие сервисы и информация, совместно используемая пользовательскими модулями.
  • «users» — всё, что касается пользователей.
  • «auth» — обработка генерации токенов JWT (JSON Web Token) и поток авторизации.

Теперь запустите « npm install » (или « yarn », если он у вас есть).

Поздравляем, теперь у вас есть все зависимости и настройки, необходимые для запуска простого бэкенда на базе REST API.

Создание пользовательского модуля

Мы будем использовать Mongoose — ODM-библиотеку для MongoDB. С её помощью создадим модель «user» (пользователь) в рамках схемы «user» (пользователь).

Первым делом нужно создать схему для библиотеки Mongoose в « /users/models/users.model.js »:

const userSchema = new Schema(< firstName: String, lastName: String, email: String, password: String, permissionLevel: Number >);

После определения схемы можно легко присоединить её к модели «user».

const userModel = mongoose.model('Users', userSchema);

Теперь можем использовать эту модель для реализации всех операций «CRUD», которые нам нужны в конечных точках Express.

Начнём с операции создания пользователя, определив маршрут в « users/routes.config.js »:

app.post('/users', [ UsersController.insert ]);

Этот код добавляется в основной файл «index.js» приложения на Express. Объект «UsersController» импортируется из нашего контроллера, где мы соответствующим образом хешируем пароль, определённый в « /users/controllers/users.controller.js »:

exports.insert = (req, res) => < let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512',salt) .update(req.body.password) .digest("base64"); req.body.password = salt + "$" + hash; req.body.permissionLevel = 1; UserModel.createUser(req.body) .then((result) =>< res.status(201).send(); >); >;

Теперь мы можем протестировать нашу модель Mongoose, запустив сервер (« npm start ») и отправив запрос «POST» в « /users » с данными в формате JSON:

Для этого есть соответствующий инструментарий. Во-первых, это Insomnia (его рассмотрим далее) и Postman — популярные инструменты с графическим интерфейсом, а также curl — утилита командной строки.

Можно даже использовать один только JavaScript. Например, из встроенной консоли средств разработки вашего браузера:

fetch('http://localhost:3600/users', < method: 'POST', headers: < "Content-type": "application/json" >, body: JSON.stringify(< "firstName": "Marcos", "lastName": "Silva", "email": "marcos.henrique@toptal.com", "password": "s3cr3tp4sswo4rd" >) >) .then(function(response) < return response.json(); >) .then(function(data) < console.log('Request succeeded with JSON response', data); >) .catch(function(error) < console.log('Request failed', error); >);

Здесь результатом валидного «POST» будет только лишь идентификатор «id» от созданного пользователя:

Нам также нужно добавить метод «createUser» к модели в « users/models/users.model.js »:

exports.createUser = (userData) => < const user = new User(userData); return user.save(); >;

Теперь надо убедиться, что пользователь существует. Для этого реализуем функцию «get user by id» для следующей конечной точки, а именно « users/:userId ».

Первым делом создаём маршрут в « /users/routes/config.js »:

app.get('/users/:userId', [ UsersController.getById ]);

Затем создаём контроллер в « /users/controllers/users.controller.js »:

exports.getById = (req, res) => < UserModel.findById(req.params.userId).then((result) =>< res.status(200).send(result); >); >;

И, наконец, добавляем метод «findById» к модели в « /users/models/users.model.js »:

exports.findById = (id) => < return User.findById(id).then((result) =>< result = result.toJSON(); delete result._id; delete result.__v; return result; >); >;

Ответ будет примерно таким:

Обратите внимание! Здесь мы видим хешированный пароль. В данном руководстве мы показываем пароль, но лучше никогда этого не делать, даже если пароль хеширован. Ещё у нас здесь есть «permissionLevel», который мы будем использовать для обработки разрешений пользователя.

Повторяя изложенный выше порядок действий, теперь можно добавить функциональность для обновления пользователя. Будем использовать операцию «PATCH», позволяющую отправлять только те поля, которые мы хотим изменить. Таким образом, мы будем отправлять любые поля, которые захотим изменить, по маршруту от «PATCH» к « /users/:userid ».

Кроме того, потребуется провести дополнительную проверку. Ведь изменения должны затрагивать только соответствующего пользователя или администратора. И только администратор должен иметь возможность менять «permissionLevel» (уровень допуска).

Пока что оставим эту часть и вернёмся к ней после того, как реализуем модуль «auth». Сейчас наш контроллер будет выглядеть так:

exports.patchById = (req, res) => < if (req.body.password)< let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); req.body.password = salt + "$" + hash; >UserModel.patchUser(req.params.userId, req.body).then((result) => < res.status(204).send(<>); >); >;

Здесь мы по умолчанию будем отправлять HTTP-код «204» без тела ответа, чтобы показать, что запрос был успешным.

И нужно будет добавить к модели метод «patchUser»:

exports.patchUser = (id, userData) => < return User.findOneAndUpdate(< _id: id >, userData); >;

Список пользователей будет реализован как «GET» в « /users/ » следующим контроллером:

exports.list = (req, res) => < let limit = req.query.limit && req.query.limit > UserModel.list(limit, page).then((result) => < res.status(200).send(result); >) >;

Соответствующим методом модели будет:

exports.list = (perPage, page) => < return new Promise((resolve, reject) => < User.find() .limit(perPage) .skip(perPage * page) .exec(function (err, users) < if (err) < reject(err); >else < resolve(users); >>) >); >;

Ответ в результирующем списке будет иметь следующую структуру:

Теперь реализуем последнюю часть, «DELETE» в « /users/:userId ». Наш контроллер для удаления:

exports.removeById = (req, res) => < UserModel.removeById(req.params.userId) .then((result)=>< res.status(204).send(<>); >); >;

Контроллер снова вернёт HTTP-код «204» с пустым телом ответа в качестве подтверждения.

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

exports.removeById = (userId) => < return new Promise((resolve, reject) =>< User.deleteMany(, (err) => < if (err) < reject(err); >else < resolve(err); >>); >); >;

Теперь у нас есть все необходимые операции для манипулирования ресурсом «user» (пользователь) и готов контроллер пользователя.

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

Создание модуля «auth»

Прежде чем обезопасить модуль «users» с помощью добавления middleware-компонентов (ПО промежуточной обработки) для обеспечения разрешений и валидации, нужно создать валидный токен текущего пользователя.

В качестве токена используем JSON web token (JWT). Это стандарт для создания токенов доступа, основанный на формате JSON, который можно использовать для безопасного выполнения пользователем нескольких запросов без повторной проверки. Мы сгенерируем JWT в ответ на предоставление пользователем валидного адреса электронной почты и пароля.

Обычно у токена есть определённое время действия: для обеспечения безопасности передачи данных каждые несколько минут создаётся новый токен. Но в данном руководстве мы воздержимся от обновления токена. Для простоты ограничимся одним-единственным токеном на каждую авторизацию.

Во-первых, создадим конечную точку для POST-запросов к ресурсу «/auth». Тело запроса будет содержать адрес электронной почты пользователя и пароль:

Прежде чем задействовать контроллер, нужно проверить пользователя в « /authorization/middlewares/verify.user.middleware.js »:

exports.isPasswordAndUserMatch = (req, res, next) => < UserModel.findByEmail(req.body.email) .then((user)=>< if(!user[0])< res.status(404).send(<>); >else< let passwordFields = user[0].password.split('$'); let salt = passwordFields[0]; let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); if (hash === passwordFields[1]) < req.body = < userId: user[0]._id, email: user[0].email, permissionLevel: user[0].permissionLevel, provider: 'email', name: user[0].firstName + ' ' + user[0].lastName, >; return next(); > else < return res.status(400).send(); > > >); >;

После этого можно перейти к контроллеру и сгенерировать JWT:

exports.login = (req, res) => < try < let refreshId = req.body.userId + jwtSecret; let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64"); req.body.refreshKey = salt; let token = jwt.sign(req.body, jwtSecret); let b = Buffer.from(hash); let refresh_token = b.toString('base64'); res.status(201).send(); > catch (err) < res.status(500).send(); > >;

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

Теперь нам нужно лишь создать маршрут и вызвать соответствующий middleware-компонент в « /authorization/routes.config.js »:

app.post('/auth', [ VerifyUserMiddleware.hasAuthValidFields, VerifyUserMiddleware.isPasswordAndUserMatch, AuthorizationController.login ]);

В ответе будет сгенерированный JWT (в поле «accessToken»):

Для обработки ошибок запроса будем использовать коды ошибок HTTP:

  • «HTTP 401» для недопустимого запроса.
  • «НТТР 403» для валидного запроса с неверным токеном или для валидного токена с недопустимыми разрешениями.

Для управления разрешениями можно использовать побитовый оператор «И» (AND, &). Если возвести каждое требуемое разрешение во вторую степень, можно рассматривать каждый бит 32-битного целого числа как одно разрешение.

Администратор может получить все разрешения, установив их значение равным « 2147483647 ». В данном случае этот пользователь будет иметь возможность получить доступ к любому маршруту.

Другой пример: пользователь, значение разрешения которого равно семи, будет иметь разрешения к правам доступа, отмеченным битами для значений 1, 2 и 4 (два в нулевой, первой и второй степени).

Middleware-компонент будет выглядеть следующим образом:

exports.minimumPermissionLevelRequired = (required_permission_level) => < return (req, res, next) => < let user_permission_level = parseInt(req.jwt.permission_level); let user_id = req.jwt.user_id; if (user_permission_level & required_permission_level) < return next(); >else < return res.status(403).send(); >>; >;

Компоненты middleware универсальны. Если уровень разрешений пользователя и необходимый уровень разрешений совпадают хотя бы в одном бите, результат будет больше нуля и действие может продолжаться. В противном случае будет возвращён HTTP-код «403».

Теперь нужно добавить middleware для аутентификации к маршрутам модуля «user» в « /users/routes.config.js »:

app.post('/users', [ UsersController.insert ]); app.get('/users', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(PAID), UsersController.list ]); app.get('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.getById ]); app.patch('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.patchById ]); app.delete('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(ADMIN), UsersController.removeById ]);

На этом завершается основная часть разработки REST API. Остаётся только всё это протестировать.

Запуск и тестирование REST API с Insomnia

В Insomnia есть хорошая бесплатная версия клиента REST. Будем использовать здесь данный инструмент как приложение, помогающее понять, что происходит с нашим API.

Лучше всего, конечно, сразу внедрять в проект тестирования кода и точную отчётность об ошибках. Но, когда данные службы недоступны, сторонний клиент REST отлично справляется с этой задачей.

Чтобы создать пользователя, нужно просто отправить с помощью «POST» необходимые поля в соответствующую конечную точку и сохранить сгенерированный идентификатор ID для последующего использования.

В ответ с API придёт идентификатор пользователя:

Теперь с помощью конечной точки « /auth/ » можно сгенерировать JWT:

В ответе мы должны получить токен:

Берём «accessToken», ставим впереди него «Bearer» (не забываем о пробеле) и добавляем его в заголовки запроса внутри «Authorization»:

Если не сделать этого, то на каждый запрос, кроме регистрации, будет возвращаться HTTP-код «401». Тем более сейчас, когда мы внедрили middleware для разрешений. Но у нас есть валидный токен, поэтому от « /users/:userId » мы получаем следующий ответ:

Ранее здесь уже упоминалось, что в учебных целях и ради простоты понимания мы показываем все поля. На практике пароль (хешированный или другой) никогда не должен быть виден в ответе.

Попробуем получить список пользователей:

Неожиданно мы получаем ответ «403».

У нашего пользователя нет разрешений на доступ к этой конечной точке. Придётся поменять «permissionLevel» пользователя с 1 до 7 (даже «5» будет достаточно, так как наши бесплатные и платные уровни разрешений представлены значениями «1» и «4» соответственно.)

Это можно сделать вручную. Например, в интерактивной консоли MongoDB (идентификатор ID поменяйте на свой):

db.users.update(,>)

Затем нужно сгенерировать новый JWT. После чего мы получим нужный ответ:

Теперь протестируем обновлённый функционал, отправив запрос «PATCH» с произвольными полями в нашу конечную точку « /users/:userId »:

В ответ должен прийти «204» в качестве подтверждения успешности операции. Однако мы можем ещё раз запросить пользователя, чтобы убедиться наверняка.

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

Новый пользователь должен будет иметь разрешения со значением « 2053 » (т. е. «2048» — ADMIN — плюс наши предыдущие «5»), чтобы иметь возможность также выполнить операцию удаления.

После того как всё будет готово и сгенерирован новый JWT, нужно будет обновить заголовок запроса «Authorization»:

Отправив запрос «DELETE» на « /users/:userId », мы должны получить ответ «204» в качестве подтверждения. Можно дополнительно убедиться в удалении, отправив запрос «« /users/ »» для получения списка всех имеющихся пользователей.

Следующие этапы создания REST API

Теперь с помощью инструментов и методов, рассмотренных в этом руководстве, вы сможете создавать простые и безопасные REST API на Node.js. Многое из того, что не является существенным для работы с REST API, было опущено. Так что не забывайте:

  • Проводить проверки валидности формы (например, чтобы электронная почта пользователя была уникальной).
  • Внедрять модульное тестирование и отчётность об ошибках.
  • Не допускать изменения уровня разрешений самими пользователями.
  • Не допускать возможности администраторам удалять самих себя.
  • Не допускать разглашения конфиденциальной информации (например, хешированных паролей).
  • Помещать пароли к токену и ключи авторизации из « common/config/env.config.js » в механизм их распределения, находящийся во внешнем хранилище.

В заключение читатель может попробовать перейти от использования в кодовой базе промисов JavaScript к применению «async/await».

Нужна надёжная база для разработки веб-приложений? Выбирайте виртуальные сервера от Eternalhost с технической поддержкой 24/7 и бесплатной защитой от DDoS!

Вызов REST API из JavaScript

Data exchange between JavaScript and a server.

В современном веб-разработке часто возникает потребность в использовании REST API — это способ, который позволяет веб-сайтам и веб-приложениям обмениваться данными с сервером. Например, возникает потребность отправить данные из формы на веб-странице на сервер или получить данные с сервера для отображения на веб-странице.

JavaScript предлагает несколько методов для взаимодействия с REST API, но самым популярным и широко используемым является метод fetch(). В данном контексте fetch() представляет собой функцию, которая позволяет осуществлять асинхронные HTTP-запросы к серверу и работать с REST API.

В базовом случае использования fetch() для отправки GET-запроса к REST API код будет выглядеть следующим образом:

fetch('https://api.example.com/items') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Ошибка:', error));

В этом коде вызывается fetch() с URL-адресом REST API, затем обрабатывается ответ от сервера и преобразуется в JSON, и затем выводится полученные данные.

Для отправки POST-запроса с данными код будет немного сложнее:

let data = ; fetch('https://api.example.com/items', < method: 'POST', headers: < 'Content-Type': 'application/json', >, body: JSON.stringify(data), >) .then(response => response.json()) .then(data => console.log(data)) .catch((error) => < console.error('Ошибка:', error); >);

В этот код добавлены дополнительные опции для метода fetch(), указывающие на использование HTTP-метода POST, добавление заголовка ‘Content-Type’ и отправку данных в формате JSON.

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

Руководство по использованию JS API

JS API предоставляет возможность разрабатывать приложения в Моем Мире и подключать внешние сайты с использованием JavaScript.

Библиотека позволяет осуществлять вызовы API прямо из браузера пользователя, минуя ваш сервер, помогая вам экономить трафик и вычислительные ресурсы. Кроме того, она дает доступ к некоторым возможностям, которые не доступны для вызова с сервера.

Подключение JS API

В IFrame-приложении или на внешнем сайте

Для использования JS API вам нужно:

  1. На домен вашего приложения или сайта загрузить файл receiver.html
  2. На вкладке Настройки IFrame управления приложением или в интерфейсе редактирования сайта указать адрес загруженного файла receiver.html
  3. Подключить загрузчик библиотеки, добавив следующий код в элемент всех страниц, где вы хотите использовать JS API:

  • для приложений: вызвав функцию mailru.app.init(private_key)
  • для сайтов: вызвав функцию mailru.connect.init(app_id, private_key)

Важно! Для корректной работы JS API на многостраничных приложениях необходимо передавать на внутренние страницы все GET-параметры, которые приложение получает на главной странице. Это не относится к сайтам.

Файл receiver.html

Файл receiver.html нужен для кроссдоменной передачи данных через JavaScript в старых браузерах. Он статический и мы не планируем его менять. Загрузите его на ваш сервер и он подойдет для всех приложений и сайтов, которые работают на одном домене.

Мы проверяем содержимое файла receiver.html на побитовое совпадение с оригиналом, поэтому размещать на сервере нужно исходный файл с нашего сервера, без обработки его текстовыми редакторами (они могут вставлять BOM или делать другие преобразования, которые приведут к ошибке при проверке ресивера).

Если вы разрабатываете приложение или сайт и ваше тестовое окружение не доступно из интернета, то мы не сможем проверить правильность файла receiver.html на ваших внутренних серверах и сообщим вам об этом в интерфейсе управления. Однако, адрес должен сохраниться в настройках и будет использоваться в работе JS API. Просто при выкладывании вашего проекта в публичный доступ проверьте, что receiver.html загружен и указан в настройках правильно.

Пример кода приложения
   
Пример кода сайта
   

Во Flash-приложении

Вы можете использовать все функции JS API внутри Flash части вашего приложения. Для этого мы создали Flash API — ActionScript прослойку для вызова функций JS API. Подробнее о подключении и применении читайте на странице документации по Flash API.

Использование библиотеки

Функции JS API разделены на те, которые можно вызывать с сайтов, из приложений и общие функции (доступный и с сайтов, и из приложений). Функции для приложений сгруппированы в объекте mailru.app, для сайтов — mailru.connect, общие — в mailru.common.

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

mailru.session

Объект, в котором хранятся данные о сессии. Становится доступным после вызова mailru.app.init или mailru.connect.init

, настоятельно // рекомендуем ее проверять на сервере при // использовании данных для авторизации ss: "d41d8cd98f00b204e9800998ecf8427e", // не используется state: "", // не используется vid: "1324730981306483817" // id текущего пользователя, который вы можете // использовать для авторизации >

Про различие между vid и oid читайте в руководстве по социальным приложениям. Для внешних сайтов oid всегда равен vid.

Работа с событиями

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

Для работы с событиями используйте функции events.listen js и events.remove js . В документации функций, которые могут генерировать события, указано какие именно события генерируются.

Тестовое приложение

  • тестовое приложение
  • код тестового приложения

Функции JS API

  • mailru.app.events.like
  • mailru.app.friends.invite
  • mailru.app.friends.request
  • mailru.app.payments.showDialog
  • mailru.app.users.isAppUser
  • mailru.app.users.requireInstallation
  • mailru.app.utils.hash.read
  • mailru.app.utils.hash.write
  • mailru.app.utils.scrollTo
  • mailru.app.utils.setHeight
  • mailru.app.utils.setTitle
  • mailru.app.widget.set
  • mailru.common.friends.add
  • mailru.common.friends.getAppUsers
  • mailru.common.friends.getExtended
  • mailru.common.friends.getInvitationsCount
  • mailru.common.guestbook.post
  • mailru.common.messages.send
  • mailru.common.photos.createAlbum
  • mailru.common.photos.get
  • mailru.common.photos.getAlbums
  • mailru.common.photos.upload
  • mailru.common.photos.uploadAvatar
  • mailru.common.stream.post
  • mailru.common.users.getBalance
  • mailru.common.users.getInfo
  • mailru.common.users.hasAppPermission
  • mailru.common.users.requirePermission
  • mailru.connect.getLoginStatus
  • mailru.connect.initButton
  • mailru.connect.login
  • mailru.connect.logout
  • mailru.events.listen
  • mailru.events.remove

Другие технологии

  • REST API
  • Flash-библиотека
  • сторонние библиотеки

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *