Перевод страницы с английского оригинала. При расхождениях приоритет имеет английская версия.
Маршрутизация
DaloyJS использует префиксное дерево (trie) по сегментам с быстрым путём для статических маршрутов. После проверок безопасности пути точные статические маршруты разрешаются одним вызовом Map.get. Динамический поиск сначала предпочитает литеральные сегменты, затем параметры, затем wildcard-сегменты. Поиск без возвратов линеен по длине пути; пересекающиеся маршруты могут потребовать обхода дополнительных ветвей дерева.
Совпадением считаются только полные маршруты. Например, регистрация /users/me/details/:field не мешает /users/:id совпасть с /users/me. Внутри динамического дерева полный путь с более высоким приоритетом побеждает, даже если у него нет обработчика для запрошенного метода. Точный статический маршрут побеждает для своих зарегистрированных методов; остальные методы всё ещё могут совпасть в динамическом дереве.
Сокращённые формы
Сокращённые методы убирают лишнюю церемонию, не отказываясь от контракта. DaloyJS выводит стабильный идентификатор операции из метода и пути, например getRoot или postBookItemsByItemId. Явно заданный operationId по-прежнему имеет приоритет.
Сокращённые формы сохраняют ту же валидацию, OpenAPI, безопасность и поведение типизированного клиента, что и route(). Формы с двумя аргументами, которая молча пропускала бы контракт ответа, нет. Для намеренно непрозрачного тела явно передайте acknowledgeNoResponseBodySchema: true. Успешные «сырые» ответы, возвращаемые из preBody или beforeHandle, требуют того же подтверждения. Обычные отказы хуков с кодами 4xx/5xx его не требуют. Описания ответов остаются необязательными. Пропущенные значения превращаются в OpenAPI в стабильное описание HTTP <status> response.
Объявление маршрутов
Объявление маршрута является единым источником истины для сопоставления, валидации запроса, валидации ответа, вывода OpenAPI и типизированных клиентов. Указывайте operationId, когда используете route() для публичного маршрута, который должен попасть в типизированный клиент или сгенерированный SDK. Сокращённые HTTP-методы выводят его автоматически. DaloyJS отклоняет повторяющиеся значения operationId при регистрации.
Вывод типов в нескольких файлах
Экспортируйте каждый маршрут через defineRoute(), затем соберите импортированные контракты через registerRoutes(). Литеральный кортеж переживает границы файлов и модулей, поэтому клиент без кодогенерации сохраняет все идентификаторы операций и схемы.
Цепочка вызовов route() тоже накапливает маршруты корректно. Не ставьте голую аннотацию const app: App на итоговое собранное приложение: она явно расширяет тип и стирает кортеж маршрутов.
group() с колбэком и register() плагинов по-прежнему дают изоляцию во время выполнения, но TypeScript не может расширить тип родительской переменной изнутри их колбэков. Чтобы получить полностью типизированный клиент без кодогенерации в больших модулях, экспортируйте литеральные кортежи маршрутов и делайте registerRoutes() последней границей композиции.
HTTP-методы
Поддерживаются методы GET, POST, PUT, PATCH, DELETE, HEAD и OPTIONS. Нестандартные методы, такие как TRACE, CONNECT и глаголы WebDAV, отклоняются при регистрации.
HEAD откатывается к соответствующему маршруту GET, если явного маршрута HEAD нет, и возвращает те же заголовки с пустым телом. OPTIONS возвращает preflight-ответ 204 с заголовком Allow, если путь существует, но явный маршрут OPTIONS не зарегистрирован.
Сокращённые методы есть для GET, POST, PUT, PATCH, DELETE и HEAD. Явный маршрут OPTIONS регистрируйте через route(). В противном случае DaloyJS сам обработает preflight.
Параметры пути
Значения пути декодируются до валидации. Если схема request.params не указана, ctx.params выводится из пути как набор «сырых» строк. Конфликтующие имена параметров в одной и той же позиции дерева, например /a/:x и /a/:y, вызывают исключение при регистрации.
Имена захватов должны быть непустыми и уникальными в пределах маршрута. Имена __proto__, constructor и prototype отклоняются, чтобы объекты параметров оставались безопасными для дальнейшего использования. Отклонённая регистрация не резервирует свой operationId.
Wildcard-захваты
Завершающий сегмент *name захватывает остаток пути в одну декодированную строку и требует хотя бы одного оставшегося сегмента. Голый * использует имя wildcard. Wildcard-сегмент должен быть последним: /assets/*path/private вызывает исключение при регистрации, а не молча совпадает с более широким путём. Несколько методов могут разделять один wildcard-маршрут, если имя захвата совпадает. Повторяющиеся методы и конфликтующие имена wildcard вызывают исключение.
«Сырые» сегменты обхода пути (..), пустые сегменты // и некорректные percent-последовательности в захватах просто не совпадают. Определение методов применяет те же проверки, поэтому отклонённые пути не объявляют заголовок Allow. Начиная с 1.3.7 сегмент параметра или wildcard никогда не связывает . или .., включая закодированные варианты %2e и %2e%2e. URL-парсер разрешил бы их как шаги по каталогам, и если бы они связывались, обработчик получил бы не тот путь, который видел middleware. Захват также отклоняет декодированное значение с компонентом . или .. за закодированным разделителем (..%2F..%2Fsecret, ..%5C) или с любым управляющим символом (%00, %0A, %09, %7F). Такие запросы получают 404.
Захват, который декодирует закодированный / или \ (%2F, %5C), по умолчанию тоже отклоняется с 404, потому что одно захваченное значение тогда охватывало бы несколько сегментов пути для любого последующего потребителя: файловой системы, URL или прокси. Если маршрут действительно использует идентификаторы со слешем, включите это для отдельного маршрута:
Эта опция никогда не допускает обратно компоненты .. или управляющие символы. Декодированные параметры остаются недоверенными данными и не являются очищенными путями файловой системы, поэтому доступ к файловой системе проверяйте отдельно.
Адаптеры также держат роутер и middleware на одном представлении пути (начиная с 1.3.7). Адаптер Node канонизирует request-target, содержащий \, точечные сегменты или их формы %2e, так же, как это сделал бы new URL(), ещё до маршрутизации. Адаптеры Node и AWS Lambda отклоняют с 400 заголовок Host, который не является простым host[:port] (например, содержит \ / ? # @ % или пробельные символы). Поэтому защиты на основе пути, такие как except(), совпадают ровно с тем маршрутом, который выполняется. Подробности смотрите в описании адаптера Node (на английском).
Группы
Группы объединяют префиксы, теги, хуки и настройки аутентификации по умолчанию с маршрутами, зарегистрированными внутри колбэка. Дочернее приложение изолировано: middleware, добавленный внутри группы, не утекает в маршруты вне этой группы. Маршруты в группах видны маршрутизации во время выполнения и OpenAPI. О параллельных контрактах /api/v1 и /api/v2, политике миграции и отдельно генерируемых SDK читайте в руководстве по версионированию API (на английском).
Опции маршрута
request: схемы дляparams,query,headersиbody.responses: объявленные коды статуса и необязательные схемы тела и заголовков ответа.accepts: список разрешённыхContent-Typeдля маршрута со схемой тела запроса.auth: требование безопасности OpenAPI для маршрута. Используйте вместе с хуком аутентификации, напримерbearerAuth().internal: скрывает маршрут от публичных адаптеров, но разрешает вызовыapp.inject()внутри процесса.deprecatedиsunset: помечают эндпоинт как устаревший и выставляют соответствующие заголовки ответа.callbacksиmeta: добавляют колбэки OpenAPI, примеры и метаданные маршрута, удобные для ИИ.
Хуки
Хуки подключают поведение в фиксированных точках жизненного цикла:
onRequest: самый ранний, до разбора запроса.preBody: после сопоставления маршрута, до валидации схем и чтения тела. Здесь выполняется встроенная аутентификация по заголовку, JWT, basic и mTLS.beforeHandle: после валидации, перед вашим обработчиком. ВернитеResponse, чтобы прервать обработку.afterHandle: оборачивает или преобразует результат обработчика до сериализации ответа.onError: наблюдает за ответом с ошибкой или заменяет его.onSend: изменяет исходящие заголовки на месте или возвращает новыйResponse. Выполняется на путях успеха, ошибки и preflight-запросаOPTIONS.onResponse: последний наблюдатель. Используйте его для логирования и метрик. Не изменяйте здесь ответ.
- 01самый раннийonRequestдо разбора запроса
- 02самый дешёвый отказpreBodyмаршрут найден · тело не тронуто
- 03фреймворквалидацияparams · query · body · headers
- 04beforeHandleверните Response, чтобы прервать обработку
- 05обработчиклогика вашего маршрута
- 06afterHandleобернуть / преобразовать результат
- 07onSendизменить или заменить Response
- 08всегдаonResponseтолько наблюдение
Преобразование ответов через onSend
Используйте onSend, когда нужно переписать исходящий ответ: например, добавить заголовок, убрать внутренний заголовок или полностью заменить ответ. Возврат void сохраняет текущий ответ. Несколько хуков onSend выстраиваются в конвейер (глобальные -> группы -> маршрута).
onSend выполняется после валидации ответа и после слияния заголовков уровня запроса, включая x-request-id. Он выполняется до onResponse, который остаётся правильным местом для логирования и метрик.
405 Method Not Allowed
Если путь зарегистрирован для одного метода, а вызван другим, роутер возвращает 405 с корректным заголовком Allow, а не вводящий в заблуждение 404. Маршруты с internal: true исключаются из публичных ответов 405 и заголовка Allow, чтобы скрытые административные или cron-эндпоинты не раскрывались через перебор методов.
Определение методов учитывает и точный статический маршрут, и выбранный динамический маршрут. При GET /users/me и POST /users/:id путь /users/me объявляет оба зарегистрированных метода. Заголовок перечисляет явные регистрации; синтезированная обработка HEAD и OPTIONS не добавляет записей.
Производительность
Последнее обновление: