UniSet 2.44.3
(JScript): Mini HTTP router (uniset2-mini-http-router.js)

Роутер сопоставляет HTTP-запросы с обработчиками по методу и шаблону пути, поддерживает middleware, группировки маршрутов (prefix), CORS и обработчики ошибок. Роутер спроектирован для работы поверх мини‑HTTP сервера из uniset2-mini-http.js, но может использоваться и отдельно, если передать совместимые req/res.

Пример

load("uniset2-mini-http.js");
load("uniset2-mini-http-router.js");
..
const r = createRouter();
// cors
r.use(setCors({ origin: '*', methods: 'GET,POST,OPTIONS' }));
// маршруты
r.route('GET', '/ping', http_pong);
r.route('GET', '/info', http_info);
r.route('GET', '/hello', (req, res) => {
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('hello');
});
// подпрефиксы (группы)
// example: /api/v1/xxx
const api = r.group('/api');
api.route('GET', '/v1/info', http_info);
api.route('GET', '/v1/ping', http_pong);
// логирование
router.setLogger(function(ev){
// ev = { method, path, status, ms }
print(`[${ev.ms}ms] ${ev.status} ${ev.method} ${ev.path}`);
});
// server
startMiniHttp({
host: "127.0.0.1",
port: 9090,
onRequest: r.handle
});
function http_pong(req, res) {
return res.end('pong');
}
function http_info(req, res) {
mylog.info("HTTP INFO");
const info = {
script: 'main.js',
date: new Date().toISOString()
};
return res.json(info);
}

Быстрый старт

// 1) Создаём роутер
const router = createRouter();
// 2) Базовый middleware (логирование)
router.use((req, res, next) => {
// req.method, req.path, req.headers, req.query доступны до хендлеров
// Важно вызывать next() чтобы передать управление дальше
next();
});
// 3) Простой маршрут GET /ping
router.route('GET', '/ping', (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('pong');
});
// 4) Параметры пути и query: GET /user/:id
router.route('GET', '/user/:id', (req, res) => {
// req.params.id (из сегмента :id), req.query (из строки запроса)
const id = req.params.id;
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.end(JSON.stringify({ ok: true, id }));
});
// 5) Обработчик по умолчанию 404 (если ничего не сматчилось)
router.use((req, res, next) => {
if (res.headersSent) return; // если кто-то уже ответил
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Not Found');
});
// 6) Error-middleware (ловит исключения из хендлеров)
router.useError((err, req, res, next) => {
res.statusCode = 500;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Internal Error: ' + (err && err.message ? err.message : String(err)));
});

Обзор API

createRouter([options]) -> router

Создаёт экземпляр роутера.

  • options (опционально): объект с настройками роутера (приоритеты, кодеки и пр., если поддержаны текущей сборкой).

router.route(method, pattern, handler[, opts])

Регистрирует обработчик для метода и шаблона пути.

  • method: строка HTTP‑метода (GET, POST, PUT, DELETE, PATCH, ...).
  • pattern: шаблон пути. Поддерживаются сегменты :name (параметры) и * (жадный хвост). Примеры: /, /api/v1/items, /user/:id, /files/*.
  • handler(req, res, next): функция‑обработчик. Внутри доступны:
    • req.method, req.path, req.params, req.query, req.headers, req.body (если сервер распарсил тело).
    • res.statusCode, res.setHeader(name, value), res.end(data).
    • next(err?) — передать управление следующему совпавшему маршруту или error‑middleware при err.
  • opts (опционально): дополнительные опции маршрута (например, пользовательские метки/приоритет).

router.use(middleware)

Добавляет обычный middleware.

  • middleware(req, res, next): вызывается до маршрутов и может завершить ответ или вызвать next(). Полезно для логирования, аутентификации, парсеров тела, быстрых ответов на OPTIONS и т.п.

router.useError(errorMiddleware)

Добавляет error‑middleware.

  • errorMiddleware(err, req, res, next): вызывается, если в хендлере/предыдущем middleware был брошен throw или вызван next(err).

router.group(prefix, configure[, opts])

Группирует набор маршрутов под общим префиксом.

  • prefix: строка префикса, напр. /api или /v1/users.
  • configure(sub) — функция, внутри которой регистрируются маршруты на под‑роутере sub.
  • opts (опционально): опции группы. Пример:
    router.group('/api', (api) => {
    api.route('GET', '/health', (req, res) => {
    res.end('OK');
    });
    api.route('POST', '/items', createItem);
    });

setCors(options) -> middleware

Фабрика CORS‑middleware. Обычно применяется в самом начале до маршрутов.

  • options:
    • origin: строка или * (по умолчанию *).
    • methods: массив/строка допустимых методов (напр. GET,POST,OPTIONS).
    • headers: массив/строка разрешённых заголовков (напр. Content-Type,Authorization).
    • credentials: true|false (по умолчанию false). Пример:
      const cors = setCors({ origin: '*', methods: 'GET,POST,OPTIONS' });
      router.use(cors);
      // быстрая обработка preflight в обычном middleware
      router.use((req, res, next) => {
      if (req.method === 'OPTIONS') {
      res.statusCode = 204;
      res.end();
      return;
      }
      next();
      });

Сопоставление и параметры

Маршрутизатор поддерживает параметры пути (через :param), строку запроса (?a=1), а также wildcard-пути (*).

  • :param — один сегмент пути
  • * — любое окончание пути
var r = createRouter();
// параметр :id и query ?active=1
r.route('GET', '/user/:id', function(req, res){
// req.params = { id: "42" }
// req.query = { active: "1" }
res.json({ id: req.params.id, active: req.query.active });
});
// wildcard для любых под-путей
r.route('GET', '/files/*', function(req,res){
// path: /files/docs/readme.txt
// req.params = { wild: "docs/readme.txt" }
res.send('Requested: ' + req.params.wild);
});

Объект req.query

Парсер автоматически формирует объект req.query из строки запроса независимо от того, в каком поле сервера лежит URL (req.path, req.url, req.uri, req.search). Ключи соответствуют именам параметров, значения — строки или массив строк, если параметр повторяется.

// GET /api/v1/status/23?qq=12&tag=foo&tag=bar
router.route('GET', '/api/v1/status/:id', function(req, res){
// req.params.id === "23"
// req.query.qq === "12"
// req.query.tag === ["foo","bar"]
// Для обратной совместимости значения query также отражаются в req.params.*, если имя ещё не занято.
res.json({ id: req.params.id, qq: req.query.qq, tags: req.query.tag });
});

Ошибки и 404/405

Если маршрут не найден, по умолчанию возвращается 404 Not Found.
Для переопределения или добавления универсальной обработки ошибок можно использовать useError().

var r = createRouter();
// пример ошибки в маршруте
r.route('GET', '/boom', function(req,res){
throw new Error('internal failure');
});
// обработчик ошибок
r.useError(function(err, req, res, next){
res.status(500).json({ error: err.message });
});
// 404 (и 405) можно вернуть вручную
r.route('GET', '/onlyget', function(req,res){
res.send('GET OK');
});
r.route('POST', '/onlyget', function(req,res){
res.status(405).send('Method Not Allowed');
});

Советы по эксплуатации

  • Добавляйте метрики (время обработки, коды ответов) при помощи middleware.
  • Отвечайте на OPTIONS как можно раньше (204 No Content).
  • Явно устанавливайте Content-Type, даже для текстовых ответов.