Middleware
Middleware — промежуточный обработчик: код, через который проходит запрос до контроллера и ответ после него. В нём удобно делать общие проверки — «вошёл ли пользователь», «не заблокирован ли он» — и вести журнал запросов, не повторяя код в каждом контроллере.
Создание
node ace make:middleware log_requestsКоманда спросит, в какой стек добавить middleware, — выберите named. Появится файл app/middleware/log_requests_middleware.ts:
// app/middleware/log_requests_middleware.ts
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class LogRequestsMiddleware {
async handle(ctx: HttpContext, next: NextFn) {
const start = Date.now()
const output = await next()
console.log(`${ctx.request.method()} ${ctx.request.url()} — ${Date.now() - start} мс`)
return output
}
}Код до await next() выполняется перед контроллером, код после — когда ответ уже готов. Если не вызвать next(), запрос дальше не пойдёт.
Регистрация
Все middleware перечислены в start/kernel.ts. Именованные — в объекте middleware:
// start/kernel.ts
export const middleware = router.named({
guest: () => import('#middleware/guest_middleware'),
auth: () => import('#middleware/auth_middleware'),
logRequests: () => import('#middleware/log_requests_middleware'),
})Команда make:middleware уже дописала последнюю строку. Подключите middleware к маршруту:
// start/routes.ts
import { middleware } from '#start/kernel'
router.get('/posts', [PostsController, 'index']).use(middleware.logRequests())После каждого запроса к /posts в терминале сервера появится строка вроде GET /posts — 12 мс.
Три стека
| Стек | Где регистрируется | Когда работает |
|---|---|---|
| Сервер | server.use([...]) | На каждый запрос, даже без маршрута |
| Роутер | router.use([...]) | На каждый найденный маршрут |
| Именованные | router.named({...}) | Только там, где подключены через .use() |
Защита группы маршрутов
Чаще всего именованные middleware подключают к группе. Все страницы управления статьями доступны только вошедшим:
// start/routes.ts
router
.group(() => {
router.get('/posts/create', [PostsController, 'create']).as('posts.create')
router.post('/posts', [PostsController, 'store']).as('posts.store')
})
.use(middleware.auth())Гость, открывший /posts/create, попадёт на страницу входа — адрес для перенаправления задан в app/middleware/auth_middleware.ts свойством redirectTo.
Пользователь на каждой странице
Чтобы auth.user был известен и на публичных страницах, нужен middleware, который просто проверяет сессию. Создайте его командой node ace make:middleware silent_auth и выберите стек router:
// app/middleware/silent_auth_middleware.ts
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class SilentAuthMiddleware {
async handle(ctx: HttpContext, next: NextFn) {
await ctx.auth.check()
return next()
}
}check не выбрасывает ошибку для гостей, а только заполняет auth.user, если пользователь вошёл. Команда добавит middleware в стек роутера после инициализации auth:
// start/kernel.ts
router.use([
() => import('@adonisjs/core/bodyparser_middleware'),
() => import('@adonisjs/session/session_middleware'),
() => import('@adonisjs/shield/shield_middleware'),
() => import('@adonisjs/auth/initialize_auth_middleware'),
() => import('#middleware/silent_auth_middleware'),
])Теперь шапка блога показывает имя пользователя на любой странице.
Частая ошибка
Перед next() забыли await. Тогда код «после» выполняется раньше, чем контроллер закончил работу: замер времени показывает 0 мс, а ошибки контроллера проходят мимо middleware. Всегда пишите const output = await next() и возвращайте output, как в примере.