Маршрутизация в Node.js

В Node.js маршрутизация — ключевая часть обработки HTTP-запросов. Она определяет, как распределять запросы на основе разных URL-адресов и HTTP-методов (таких как GET, POST, PUT, DELETE и т. д.).

Маршрутизация обычно используется для создания веб-приложений, особенно RESTful API.

Сам Node.js не имеет встроенных механизмов маршрутизации, но их можно реализовать через библиотеки промежуточного ПО (такие как Express).

Маршрутизация обычно включает следующие аспекты:

  • Сопоставление URL: Сопоставлять правила маршрутизации в соответствии с запрошенным URL.
  • Сопоставление HTTP-метода: Сопоставлять правила маршрутизации в соответствии с запрошенным HTTP-методом (GET, POST, PUT, DELETE и т. д.).
  • Обработка запроса: Как только найдено подходящее правило маршрутизации, вызывается соответствующая функция-обработчик для обработки запроса.

В Node.js мы можем создать простой маршрут с помощью модуля http, как в следующем примере:

Пример

const http = require('http');

// Создать сервер и определить маршруты
const server = http.createServer((req, res) => {
  const { url, method } = req;

  if (url === '/' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Home Page');
  } else if (url === '/about' && method === 'GET') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('About Page');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('404 Не найдено');
  }
});

server.listen(3000, () => {
  console.log('Сервер работает на http://localhost:3000');
});

Доступ через браузерhttp://localhost:3000Отображается следующим образом:

Доступ через браузерhttp://localhost:3000/aboutОтображается следующим образом:

Если обратиться по другим URL-адресам, сразу отобразится404 Not Found。

Для получения дополнительной информации об HTTP-запросах см.:Учебник по HTTP。


Параметры запроса

Полный URLhttp://localhost:8888/start?foo=bar&hello=worldсодержит хост, путь и строку запроса.

Чтобы разобрать эти данные, можно использовать объектURLи модульquerystring.

const myUrl = new URL("http://localhost:8888/start?foo=bar&hello=world");

// 提取路径名
console.log(myUrl.pathname); // 输出: /start

// 提取查询参数
console.log(myUrl.searchParams.get("foo"));   // 输出: bar
console.log(myUrl.searchParams.get("hello")); // 输出: world
myUrl.pathname
          |
          |
        -----
http://localhost:8888/start?foo=bar&hello=world
                                ---       -----
                                 |          |
                                 |          |
           myUrl.searchParams.get("foo")    |
                                            |
                                     myUrl.searchParams.get("hello")

Конечно, мы также можем использовать модуль querystring для разбора параметров в теле POST-запроса. Об этом будет рассказано позже вЗапросы GET/POST в Node.js.

Все необходимые данные запроса будут содержаться в объекте request, который передается в качестве первого параметра в функцию обратного вызова onRequest().

Теперь давайте добавим немного логики в функцию onRequest() модуля http, чтобы определить путь URL, запрошенный браузером:

Код файла server.js:

var http = require("http"); var url = require("url"); function start() { function onRequest(request, response) { //Используйте конструктор URL для разбора пути запроса const pathname = new URL(request.url, `http://${request.headers.host}`).pathname; console.log(`Запрос для ${pathname} получен.`); //Выведите путь запроса //Установите заголовок ответа и содержимое ответа response.writeHead(200, { "Content-Type": "text/plain" }); //Установите код состояния и тип содержимого response.write("Привет, мир"); //Отправьте содержимое ответа клиенту response.end(); //Завершите ответ } //Создайте сервер и слушайте указанный порт http.createServer(onRequest).listen(8888); console.log("Сервер запущен."); //Выведите сообщение о запуске сервера } //Экспортируйте функцию start для использования другими модулями module.exports.start = start;

Хорошо, наше приложение теперь может различать разные запросы на основе запрошенного URL-пути — это позволяет использовать маршрутизацию (пока не завершённую) для сопоставления запросов с обработчиками на основе URL-пути.

В приложении, которое мы собираемся построить, это означает, что запросы из /start и /upload можно обрабатывать разным кодом. Позже мы увидим, как всё это сочетается.

Теперь мы можем написать маршрут. Создайте файл с именемrouter.jsи добавьте следующее содержимое:

Код файла router.js:

function route(pathname) { console.log("Собираемся маршрутизировать запрос для " + pathname); } //Экспорт функции route exports.route = route;

router.js обрабатывает логику маршрутизации, определяет и экспортирует функцию route, которая используется для обработки разных путей, когда сервер получает запрос.

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

Сначала расширим функцию start() сервера, чтобы функцию route можно было передать как параметр.server.jsКод файла выглядит так:

Код файла server.js:

// server.js const http = require("http"); //Импортируйте модуль http Node.js для создания сервера const { URL } = require("url"); //Импортируйте конструктор URL из модуля url //Определите и экспортируйте функцию start для запуска сервера function start(route) { //Определите функцию onRequest для обработки каждого запроса function onRequest(request, response) { //Используйте конструктор URL для разбора пути запроса const pathname = new URL(request.url, `http://${request.headers.host}`).pathname; console.log(`Запрос для ${pathname} received.`); //Выведите путь запроса route(pathname); //Вызовите функцию route для обработки пути //Установите заголовок ответа и содержимое ответа response.writeHead(200, { "Content-Type": "text/plain" }); response.write("Hello World"); response.end(); } //Создайте сервер и прослушивайте указанный порт http.createServer(onRequest).listen(8888); console.log("Сервер запущен."); } //Экспортируйте функцию start для использования другими модулями module.exports.start = start;

server.js определяет логику запуска сервера и вызывает функцию route при получении запроса.

В то же время мы соответствующим образом расширим index.js, чтобы функцию route можно было внедрить в сервер:

Код файла index.js:

var server = require("./server"); var router = require("./router"); server.start(router.route);

index.js — это входной файл программы, отвечающий за запуск сервера и передачу модуля маршрутизации в модуль сервера.

Теперь запустите приложение (node index.js, всегда помните эту командную строку), затем запросите URL. Вы увидите, что приложение выводит соответствующую информацию, которая показывает, что наш HTTP-сервер уже использует модуль маршрутизации и передаст путь запроса в route:

$ node index.js
Server has started.

Откройте в браузере следующий URLhttp://localhost:8888/, и сервер должен вывести в консоль сообщение, связанное с route, для этого пути, и вернуть ответ "Hello World":

В терминале бэкенда будет отображаться информация о доступе:

Request for / received.
About to route a request for /
Request for /favicon.ico received.
About to route a request for /favicon.ico

Маршрутизация с Express

Express — популярный фреймворк Node.js, предоставляющий мощные возможности маршрутизации.

Подробнее об Express см.:Фреймворк Node.js Express。

Установка Express

Сначала убедитесь, что у вас установлен Express. Если нет, вы можете использовать npm для его установки:

npm install express

Базовая маршрутизация

Ниже приведено простое приложение Express, которое демонстрирует, как настроить базовую маршрутизацию:

Пример

const express = require('express');
const app = express();
const port = 3000;

// Определить GET-маршрут
app.get('/', (req, res) => {
    res.send('Привет, мир!');
});

// Определить POST-маршрут
app.post('/submit', (req, res) => {
    res.send('Форма отправлена!');
});

// Запустить сервер
app.listen(port, () => {
    console.log(`Сервер запущен на http://localhost:${port}`);
});

Динамическая маршрутизация

Динамическая маршрутизация позволяет использовать параметризованные URL.

Например, вы можете определить маршрут для обработки/users/:id, где:idявляется динамическим параметром.

app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`User ID: ${userId}`);
});

Параметры маршрута

Express позволяет извлекать параметры из URL и получать доступ к ним через объект req.params.

Пример

app.get('/users/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`ID пользователя: ${userId}`);
});

app.get('/search/:query', (req, res) => {
    const query = req.params.query;
    res.send(`Поисковый запрос: ${query}`);
});

Параметры запроса

Параметры запроса — это пары ключ-значение в URL, обычно используемые для передачи дополнительной информации. Вы можете получить доступ к параметрам запроса через объект req.query.

app.get('/search', (req, res) => {
    const query = req.query.q;
    res.send(`Search query: ${query}`);
});

Промежуточное ПО маршрута

Промежуточное ПО маршрута — это функции, выполняемые до или после обработки запроса. Вы можете использовать промежуточное ПО для выполнения таких задач, как аутентификация, логирование и т. д.

// 日志记录中间件
const logger = (req, res, next) => {
    console.log(`Request Type: ${req.method} ${req.url}`);
    next();
};

// 使用中间件
app.use(logger);

app.get('/', (req, res) => {
    res.send('Hello, World!');
});

Группировка маршрутов

Чтобы лучше организовать свой код, вы можете использовать группировку маршрутов. Express предоставляет объект express.Router, который можно использовать для создания модульных, монтируемых обработчиков маршрутов.

Пример

const express = require('express');
const app = express();
const port = 3000;

// Создать экземпляр маршрутизатора
const userRouter = express.Router();

// Определить маршруты, связанные с пользователями
userRouter.get('/', (req, res) => {
    res.send('List of users');
});

userRouter.get('/:id', (req, res) => {
    const userId = req.params.id;
    res.send(`ID пользователя: ${userId}`);
});

// Подключить маршрутизатор пользователей
app.use('/users', userRouter);

// Запустить сервер
app.listen(port, () => {
    console.log(`Сервер запущен на http://localhost:${port}`);
});

Продвинутые методы маршрутизации

1. Обработка ошибок:Вы можете определить промежуточное ПО для обработки ошибок, чтобы перехватывать и обрабатывать ошибки в маршрутах.

app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
});

2. Приоритет маршрутов:Порядок, в котором определяются маршруты, определяет их приоритет; маршруты, определённые первыми, будут сопоставлены первыми.

3. Ограничения маршрутов:Вы можете использовать промежуточное ПО для ограничения доступа к определённым маршрутам, например, разрешая доступ только аутентифицированным пользователям.

Пример

const authMiddleware = (req, res, next) => {
    if (req.headers.authorization) {
        Далее();
    } else {
        res.status(401).send('Unauthorized');
    }
};

app.get('/admin', authMiddleware, (req, res) => {
    res.send('Admin page');
});
Другие расширения