JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Создание простого веб-сервера с несколькими маршрутами

Создание простого веб-сервера с несколькими маршрутами

Модуль 4: Node.js, Next.js и Angular
4 уровень , 9 лекция
Открыта

1. Зачем нужны маршруты на сервере?

Если вы когда-нибудь пользовались браузером (а если нет — как вы вообще читаете эту лекцию?), то наверняка замечали, что сайты бывают многостраничными: у них есть главная страница, страница "О нас", список товаров, отдельные карточки товаров и так далее. Всё это — разные маршруты.

В мире серверов "маршрут" — это комбинация URL (пути, например, /, /about, /products) и HTTP-метода (GET, POST и др.). Сервер должен уметь отличать запросы друг от друга и отдавать правильные ответы.

В прошлой лекции наш сервер был похож на робота с одной репликой: "Привет, я сервер!". Сегодня мы научим его говорить разные вещи в зависимости от того, что у него спрашивают.

Базовая структура сервера с маршрутизацией

Начнём с простого примера: сервер, который умеет отвечать на три маршрута:

  • / — главная страница (GET)
  • /about — страница "О нас" (GET)
  • /api/time — отдаёт текущее время в формате JSON (GET)

Скелет сервера

Создайте файл server.js и напишите:

const http = require('http');

const PORT = 3000;

const server = http.createServer((req, res) => {
  // Здесь будет обработка маршрутов
});

server.listen(PORT, () => {
  console.log(`Сервер запущен на http://localhost:${PORT}`);
});

Теперь добавим обработку маршрутов.

2. Разбор запроса: req.url и req.method

В колбэке createServer вы получаете два объекта:

  • req — объект запроса, содержит информацию о том, что хочет пользователь (метод, путь, заголовки, тело)
  • res — объект ответа, через который вы отправляете данные обратно

Для маршрутизации нам нужны два свойства:

  • req.url — путь запроса, например, /, /about, /api/time
  • req.method — HTTP-метод (GET, POST, ...)

Давайте выведем их в консоль для наглядности:

const server = http.createServer((req, res) => {
  console.log('Поступил запрос:', req.method, req.url);
  // ...
});

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

3. Простая маршрутизация: if/else

Самый прямолинейный способ обработки маршрутов — это обычные if/else if/else:

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end('<h1>Главная страница</h1>');
  } else if (req.method === 'GET' && req.url === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end('<h1>О нас</h1><p>Это очень крутой сервер на Node.js!</p>');
  } else if (req.method === 'GET' && req.url === '/api/time') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ time: new Date().toISOString() }));
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end('404 Not Found');
  }
});

Что здесь происходит?

  • Мы проверяем метод и путь запроса.
  • Для каждого маршрута отправляем свой ответ и нужный заголовок.
  • Если маршрут не найден — отправляем 404.

4. Улучшаем маршрутизацию: парсинг URL

В реальных приложениях часто нужно обрабатывать параметры в URL (например, /products/42). Для этого удобно использовать модуль url или встроенный класс URL.

В Node.js 10+ можно так:

const { URL } = require('url');

const server = http.createServer((req, res) => {
  const parsedUrl = new URL(req.url, `http://${req.headers.host}`);

  if (req.method === 'GET' && parsedUrl.pathname === '/') {
    // ...
  }
  // и так далее
});

Теперь вместо req.url мы используем parsedUrl.pathname — это путь без query-строки (/api/time?format=short/api/time).

5. Пример: сервер с несколькими маршрутами

Давайте соберём всё вместе. Вот полный пример сервера с несколькими маршрутами:

const http = require('http');
const { URL } = require('url');

const PORT = 3000;

const server = http.createServer((req, res) => {
  const parsedUrl = new URL(req.url, `http://${req.headers.host}`);

  // Главная страница
  if (req.method === 'GET' && parsedUrl.pathname === '/') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end('<h1>Главная страница</h1><a href="/about">О нас</a>');
    return;
  }

  // О нас
  if (req.method === 'GET' && parsedUrl.pathname === '/about') {
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    res.end('<h1>О нас</h1><p>Этот сервер написан на чистом Node.js!</p><a href="/">На главную</a>');
    return;
  }

  // API: текущее время
  if (req.method === 'GET' && parsedUrl.pathname === '/api/time') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ time: new Date().toLocaleTimeString() }));
    return;
  }

  // 404 Not Found
  res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('404 Not Found');
});

server.listen(PORT, () => {
  console.log(`Сервер запущен на http://localhost:${PORT}`);
});

Как проверить?

Добавляем ещё один маршрут

В качестве практики добавим новый маршрут: /hello?name=Иван — возвращает приветствие с именем из query-параметра.

Как это сделать?

  1. Парсим query-параметры через parsedUrl.searchParams.
  2. Получаем значение параметра name.
if (req.method === 'GET' && parsedUrl.pathname === '/hello') {
  const name = parsedUrl.searchParams.get('name') || 'Гость';
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.end(`<h1>Привет, ${name}!</h1>`);
  return;
}

Теперь если вы откроете http://localhost:3000/hello?name=Иван, сервер ответит: "Привет, Иван!".

6. Кратко о поддержке разных методов (GET, POST)

Пока что мы обрабатывали только GET-запросы. Если хотите добавить поддержку, например, POST-запроса на /api/echo, можно сделать так:

if (req.method === 'POST' && parsedUrl.pathname === '/api/echo') {
  let body = '';
  req.on('data', chunk => body += chunk);
  req.on('end', () => {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ youSent: body }));
  });
  return;
}

Теперь можно отправить POST-запрос с помощью Postman или curl, и сервер вернёт то, что вы ему отправили.

7. Типичные ошибки при создании простого сервера с маршрутами

Ошибка №1: не учитывается query-строка.
Если сравнивать req.url напрямую с /about, а пользователь зашёл на /about?utm=123, такой запрос не обработается. Лучше всегда использовать parsedUrl.pathname.

Ошибка №2: забыли выставить правильный Content-Type.
Если отправить JSON, но не указать application/json, браузер может не понять, что это. Аналогично с HTML.

Ошибка №3: не завершён ответ (res.end).
Если забыть вызвать res.end(), браузер будет ждать ответа вечно, а сервер может зависнуть.

Ошибка №4: не обрабатываются методы кроме GET.
Если сервер должен поддерживать POST, PUT и др., не забывайте проверять req.method.

Ошибка №5: дублирование кода при большом количестве маршрутов.
Для 2-3 маршрутов if/else достаточно, но если их становится больше — код становится нечитаемым. Для сложных серверов лучше использовать фреймворки (например, Express.js), но для учебных целей if/else — отличный старт.

1
Задача
Модуль 4: Node.js, Next.js и Angular, 4 уровень, 9 лекция
Недоступна
Отдача JSON-данных по API-маршруту
Отдача JSON-данных по API-маршруту
1
Задача
Модуль 4: Node.js, Next.js и Angular, 4 уровень, 9 лекция
Недоступна
Обработка query-параметра и POST-запроса
Обработка query-параметра и POST-запроса
3
Опрос
Ручная маршрутизация, 4 уровень, 9 лекция
Недоступен
Ручная маршрутизация
Ручная маршрутизация
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ