Часть 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.
