Часть 3. Создание REST API: обработка запросов GET
В предыдущей статье вы установили соединение с базой данных.
В этой же добавите логику маршрутизации, контроллера и базы данных для обработки HTTP-запроса GET на конечную точку API «employees».
Добавление логики маршрутизации
Express поставляется с классом Router, который упрощает маршрутизацию HTTP-запросов к соответствующей логике контроллера. Пути маршрутов определяют конечные точки URL API и могут содержать параметры маршрута, которые фиксируют значения в URL.
Существует множество способов определить маршруты для вашего приложения. Например, когда приложение запускается, вы можете прочитать все файлы в каталоге контроллеров и автоматически сгенерировать логику маршрутизации на основе некоторых предопределенных правил, таких как имена файлов и свойства, которые они предоставляют. Кроме того, вы можете добавить файл в каталог конфигурации и прочитать его во время запуска.
В этом приложении вы будете использовать немного более низкоуровневый подход, программно определяя маршруты через новый модуль маршрутизатора. Создайте новый файл с именем router.js в каталоге services. Добавьте следующий код в файл и сохраните изменения.
const express = require('express');
const router = new express.Router();
const employees = require('../controllers/employees.js');
router.route('/employees/:id?')
.get(employees.get);
module.exports = router;
Модуль маршрутизатора начинается с того что запрашивается модуль Express, а затем создает новый экземпляр класса Router Express. Метод route модуля router используется для определения маршрута на основе переданных данных. Путь включает необязательный (из-за знака вопроса) параметр с именем id. Маршрут, возвращаемый из router, имеет методы, которые соответствуют методам HTTP и позволяют определять обработчики. В этом случае метод get используется для сопоставления входящего запроса GET с функцией get, определенной в контроллере сотрудников (которая будет создана ниже).
На данный момент у вас есть маршрутизатор, но он не используется в приложении. Чтобы использовать его, откройте файл services / web-server.js и удалите строку вверху, для которой требуется модуль базы данных (он использовался только для тестирования в предыдущем посте). Добавьте следующую строку кода на его место.
// *** line that requires ../config/web-server.js is here ***
const router = require('./router.js');
Затем используйте следующий код для замены всего обработчика app.get, который отвечает на запросы GET с использованием модуля базы данных (все 7 строк).
// *** line that adds morgan to app here ***
// Mount the router at /api so all its routes start with /api
app.use('/api', router);
Теперь router запрашивается в модуль веб-службы и «монтируется» в / api. Это означает, что полным URL-адресом для конечной точки сотрудников будет http: // server: port / api / employee /: id.
Добавление логики контроллера
Логика контроллера вступит в работу с момента, когда конечная точка URL и метод HTTP известны. Поскольку веб-сервер построен с использованием Express, логика контроллера будет определяться с помощью специального middleware или функций, которые имеют доступ к request и response объектам, а также к функции next.
Функция промежуточного программного обеспечения (middleware) будет использовать входные данные из объекта request для генерации ответа, который отправляется в response объект. Функция next обычно используется для вызова следующей функции промежуточного программного обеспечения в конвейере. Однако в этом API логика контроллера будет последним шагом в конвейере и завершит HTTP-ответ. Функция next будет вызываться только в случае возникновения ошибки, которая передает управление в стандартный обработчик ошибок Express.
Перейдите в каталог controllers и создайте новый файл с именем employee.js. Скопируйте и вставьте следующий код в файл и сохраните изменения.
/*01*/const employees = require('../db_apis/employees.js');
/*02*/
/*03*/async function get(req, res, next) {
/*04*/ try {
/*05*/ const context = {};
/*06*/
/*07*/ context.id = parseInt(req.params.id, 10);
/*08*/
/*09*/ const rows = await employees.find(context);
/*10*/
/*11*/ if (req.params.id) {
/*12*/ if (rows.length === 1) {
/*13*/ res.status(200).json(rows[0]);
/*14*/ } else {
/*15*/ res.status(404).end();
/*16*/ }
/*17*/ } else {
/*18*/ res.status(200).json(rows);
/*19*/ }
/*20*/ } catch (err) {
/*21*/ next(err);
/*22*/ }
/*23*/}
/*24*/
/*25*/module.exports.get = get;
Строка 1: API базы данных сотрудников (будет создана ниже).
Строки 3-23: объявлена асинхронная функция с именем get. Блок try-catch используется в теле функции для перехвата исключений, генерируемых в основном потоке, и передачи их следующей функции.
Строки 5-7: объявлена константа с именованным контекстом — это универсальный объект, который будет содержать свойства, относящиеся к методу поиска API базы данных. Свойство id добавляется в контекст на основе значения, которое приходит через req.params.id.
Строка 9: Метод find используется для извлечения соответствующих записей employees в базе данных.
Строки 11-19: условная логика используется для определения правильного кода состояния HTTP и тела ответа. Если один сотрудник был запрошен, но не найден, в качестве ответа отправляется код ошибки «404 Not Found». В противном случае отправляется код «200 OK» вместе с основанным на JSON телом ответа.
Строка 25: экспорт модуля чтоб можно добавить к другим модулям
Объект req.params — это всего лишь одно из нескольких свойств, используемых для получения данных из объекта входящего запроса. Другие общие свойства включают req.query для значений строки запроса в URL, req.body для тела запроса и req.cookies. Заголовки HTTP можно получить с помощью метода req.get.
Добавление логики базы данных
Чтобы запустить модуль базы данных сотрудников, перейдите в каталог db_apis и создайте новый файл с именем employee.js. Добавьте следующий код в файл.
const database = require('../services/database.js');
const baseQuery =
`select employee_id "id",
first_name "first_name",
last_name "last_name",
email "email",
phone_number "phone_number",
hire_date "hire_date",
job_id "job_id",
salary "salary",
commission_pct "commission_pct",
manager_id "manager_id",
department_id "department_id"
from employees`;
async function find(context) {
let query = baseQuery;
const binds = {};
if (context.id) {
binds.employee_id = context.id;
query += `\nwhere employee_id = :employee_id`;
}
const result = await database.simpleExecute(query, binds);
return result.rows;
}
module.exports.find = find;
Модуль базы данных сотрудников вводит общий модуль базы данных и инициализирует константу с именем baseQuery для SQL-запроса в таблице сотрудников. Псевдонимы столбцов в двойных кавычках используются для управления регистром возвращаемых ключей.
Затем объявляется функция с именем find, которая используется для выполнения запроса и возврата извлеченных строк. Если переданный параметр context имеет значение «истинного» id, то к запросу добавляется предложение where, так что возвращается только один сотрудник.
Обратите внимание, что значение context.id не было добавлено к запросу напрямую. Вместо этого использовался местозаполнитель с именем: employee_id — это называется bind variable. Использование переменных связывания с базой данных Oracle очень важно с точки зрения безопасности и производительности. Значение bind variable присваивается объекту binds, который передается вместе с запросом в database.simpleExecute. Наконец, строки, полученные из базы данных, возвращаются вызывающей стороне.
Запустите приложение и перейдите в браузер по адресу http: // localhost: 3000 / api / employee. Вы должны увидеть список сотрудников следующим образом (я свернул пару):
Вы можете выбрать одного сотрудника, добавив идентификатор в конец URL-адреса, например: http: // localhost: 3000 / api / employee / 100.
На этом этапе ваш API может обрабатывать запросы GET на конечной точке сотрудников. В следующем посте вы расширите функциональность CRUD, добавив логику, которая обрабатывает запросы POST, PUT и DELETE.