Middleware: фильтры запросов

Middleware — это слои-фильтры, через которые проходит каждый запрос на пути к контроллеру и ответ на пути обратно.

Суть: middleware проверяет или изменяет запрос до контроллера (авторизация, CSRF) и ответ после (заголовки, кэш). В Laravel 11 они регистрируются в bootstrap/app.php.

Представьте проходную в офис: прежде чем попасть к рабочему месту, вы показываете пропуск охраннику, проходите рамку, регистрируетесь. Middleware — это те самые «проходные» для HTTP-запросов. Прежде чем запрос дойдёт до контроллера, он проходит цепочку фильтров: проверяется аутентификация, валидируется CSRF-токен, стартует сессия. А ответ на пути обратно тоже проходит фильтры — например, добавляются заголовки безопасности.

Laravel из коробки даёт множество middleware: auth (пускает только авторизованных), guest (только гостей), throttle (ограничивает частоту запросов), проверку CSRF. Вы можете писать и свои — например, проверку роли «администратор».

Как работает под капотом: модель «луковицы»

Middleware работают по принципу луковицы (onion): запрос проходит слои внутрь до контроллера, а ответ выходит наружу через те же слои в обратном порядке.

        ВХОД (запрос)
  +---------------------------+
  | StartSession              |
  |  +---------------------+  |
  |  | VerifyCsrfToken     |  |
  |  |  +---------------+  |  |
  |  |  | Authenticate  |  |  |
  |  |  |  +---------+  |  |  |
  |  |  |  |CONTROLLER| |  |  |
  |  |  |  +---------+  |  |  |
  |  |  +---------------+  |  |
  |  +---------------------+  |
  +---------------------------+
        ВЫХОД (ответ)

Каждый middleware решает: пропустить запрос дальше (вызвать $next) или остановить его — например, перенаправить гостя на страницу логина.

Создание своего middleware

php artisan make:middleware EnsureUserIsAdmin
<?php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsureUserIsAdmin
{
    public function handle(Request $request, Closure $next)
    {
        if (! $request->user()?->is_admin) {
            abort(403, 'Доступ только для администраторов');
        }
        // пропускаем запрос дальше по цепочке
        return $next($request);
    }
}

В Laravel 11 псевдонимы middleware регистрируются в bootstrap/app.php:

<?php
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,
    ]);
})

А применяется middleware к маршрутам через ->middleware():

<?php
Route::get('/dashboard', [DashboardController::class, 'index'])
    ->middleware(['auth', 'admin']);

Смоделируем цепочку middleware на Python — каждый фильтр либо пропускает запрос, либо останавливает его.

Попробуй сам ▶

# Цепочка middleware (модель луковицы)
def check_auth(request, nxt):
    if not request.get('logged_in'):
        return '302 -> /login'
    return nxt(request)

def check_admin(request, nxt):
    if not request.get('is_admin'):
        return '403 Forbidden'
    return nxt(request)

def controller(request):
    return '200 OK: панель администратора'

def run(request, stack, ctrl):
    def chain(i):
        if i == len(stack):
            return ctrl(request)
        return stack[i](request, lambda r: chain(i + 1))
    return chain(0)

stack = [check_auth, check_admin]
print(run({'logged_in': True,  'is_admin': True},  stack, controller))
print(run({'logged_in': True,  'is_admin': False}, stack, controller))
print(run({'logged_in': False}, stack, controller))

Частые ошибки

  • Забыть return $next($request). Без этого запрос не дойдёт до контроллера и зависнет.
  • Тяжёлая логика в middleware. Middleware должны быть быстрыми фильтрами, а не местом для бизнес-логики.
  • Неверный порядок. Например, проверка роли до проверки аутентификации — пользователь ещё не известен.

Best practices

  • Применяйте middleware группами через Route::middleware([...])->group().
  • Держите каждый middleware маленьким и с одной зоной ответственности.
  • Используйте встроенные auth, throttle, verified вместо самописных, где можно.

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

Проверьте себя
1. По какому принципу работают middleware?
AВыполняются только после контроллера
BКак луковица: запрос идёт внутрь к контроллеру, ответ выходит наружу через те же слои
CВыполняются параллельно
DЗаменяют контроллер
2. Что произойдёт, если в middleware забыть return $next($request)?
AНичего, всё работает
BЗапрос не дойдёт до контроллера
CУскорится обработка
DУдалится маршрут