404 Not Found

404 Not Found


nginx

Uma Análise Profunda do Middleware Laravel

Middleware é o "pipeline de postos de verificação de segurança" do Laravel—cada requisição passa por camadas de verificações, como bagagem; as que falham são interceptadas imediatamente, enquanto as que passam podem prosseguir para a próxima etapa.

1. O Que Você Vai Aprender


2. Uma História Real de um Arquiteto

(1) Dor: Todas as verificações de segurança estão concentradas no controller

Bob escreveu 10 linhas de código de validação no início de cada método do controller—verificações de autenticação, isolamento de tenant, limite de requisições, CORS e logging. 50 métodos x 10 linhas = 500 linhas de código duplicado. Quando Alice adicionou uma nova política de segurança, ela teve que fazer alterações em 50 lugares; ela perdeu três, o que levou a uma vulnerabilidade de segurança. Charlie perguntou: "Vocês já ouviram falar de middleware?"

(2) A Solução com Middleware

O middleware extrai a lógica de validação comum em classes separadas, e cada requisição passa automaticamente por múltiplas camadas de middleware—autenticação, limite de requisições, CORS e isolamento de tenant—enquanto os controllers focam apenas na lógica de negócio.

PHP
// Antes — 10 linhas de verificações em cada método
public function index() {
    if (!auth()->check()) abort(401);
    if (!tenant()->isActive()) abort(403);
    if (RateLimiter::tooManyAttempts(...)) abort(429);
    // ... finalmente, lógica de negócio
}

// Depois — middleware gerencia todas as verificações
Route::middleware(['auth', 'tenant.resolve', 'throttle:60,1'])
    ->get('/shops', [ShopController::class, 'index']);
// Controller tem apenas lógica de negócio

(3) Resultado

Depois que Bob implementou o middleware, o código do controller foi reduzido em 60%; a nova política de segurança de Alice exigiu modificar apenas um middleware, com zero falhas.


3. Fluxo de Execução do Pipeline de Middleware

(1) O Processo Completo de uma Requisição Passando pelo Middleware

100%
flowchart LR
    A[Requisição] --> B[MW Global 1]
    B --> C[MW Global 2]
    C --> D[MW de Rota 1]
    D --> E[MW de Rota 2]
    E --> F[Controller]
    F --> G[Resposta]
    G --> H[After MW 2]
    H --> I[After MW 1]
    I --> J[Cliente]

(2) O Modelo Cebola

TEXT
Requisição →
  Middleware A (antes) →
    Middleware B (antes) →
      Controller → Resposta
    Middleware B (depois) →
  Middleware A (depois) →
Resposta

Cada middleware pode:

(1) ▶ Exemplo: Entendendo o Modelo Cebola do Middleware

PHP
// app/Http/Middleware/LogRequests.php
class LogRequests
{
    public function handle(Request $request, Closure $next): Response
    {
        // BEFORE — registrar requisição recebida
        Log::info('Request:', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'ip' => $request->ip(),
        ]);

        // Passar para o próximo middleware
        $response = $next($request);

        // AFTER — registrar status da resposta
        Log::info('Response:', [
            'status' => $response->getStatusCode(),
            'duration' => defined('LARAVEL_START') ? round((microtime(true) - LARAVEL_START) * 1000) : null,
        ]);

        return $response;
    }
}

Saída:

TEXT
// Execução bem-sucedida

4. Três Formas de Registro

(1) Middleware Global

Toda requisição é executada; não há necessidade de especificá-lo na rota.

PHP
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->append([
        \App\Http\Middleware\LogRequests::class,
        \App\Http\Middleware\SetLocale::class,
    ]);

    $middleware->remove([
        \Illuminate\Foundation\Http\Middleware\TrimStrings::class,
    ]);
})

(2) Middleware de Rota

Middleware especificado na definição da rota.

PHP
// Registrar alias
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'tenant' => \App\Http\Middleware\TenantResolve::class,
        'role' => \App\Http\Middleware\CheckRole::class,
        'ability' => \App\Http\Middleware\CheckAbility::class,
    ]);
})

// Usar nas rotas
Route::middleware(['auth', 'tenant', 'role:admin'])
    ->get('/admin/users', [AdminController::class, 'users']);

(3) Grupo de Middleware

Empacotar um conjunto de middleware e aplicá-lo como grupo.

Nome do Grupo Inclusões Padrão Aplicável A
web StartSession, EncryptCookies, VerifyCsrfToken Roteamento Web
api Throttle:api, SubstitueBindings Roteamento de API
PHP
// Personalizar grupos de middleware
->withMiddleware(function (Middleware $middleware) {
    $middleware->appendToGroup('api', [
        \App\Http\Middleware\TenantResolve::class,
    ]);

    $middleware->prependToGroup('web', [
        \App\Http\Middleware\SetLocale::class,
    ]);
})
Método de Registro Momento Cenários Aplicáveis
Global Todas as Requisições Logs, CORS
Alias de Rota Rota Especificada Autenticação, Tenant
Grupo de Middleware Rotas Intra-grupo Grupo Web/API

(1) ▶ Exemplo: Registro de Middleware do ShopMetrics

PHP
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    // Middleware global
    $middleware->append([
        \App\Http\Middleware\SetLocale::class,
    ]);

    // Aliases
    $middleware->alias([
        'tenant' => \App\Http\Middleware\TenantResolve::class,
        'role' => \App\Http\Middleware\CheckRole::class,
        'ability' => \App\Http\Middleware\CheckAbility::class,
    ]);

    // Adicionar ao grupo API
    $middleware->appendToGroup('api', [
        \App\Http\Middleware\EnsureJsonAccept::class,
    ]);
})

Saída:

TEXT
// Execução bem-sucedida

5. Middleware Personalizado

(1) Criar middleware

BASH
php artisan make:middleware TenantResolve
php artisan make:middleware CheckRole
php artisan make:middleware EnsureJsonAccept

(2) Middleware TenantResolve

PHP
// app/Http/Middleware/TenantResolve.php
class TenantResolve
{
    public function handle(Request $request, Closure $next): Response
    {
        $user = $request->user();
        if (!$user || !$user->tenant_id) {
            abort(403, 'No tenant associated with this account.');
        }

        $tenant = $user->tenant;

        if ($tenant->status !== 'active') {
            abort(403, 'Tenant account is ' . $tenant->status . '.');
        }

        // Armazenar tenant no container para escopo global
        app()->instance(Tenant::class, $tenant);

        // Definir contexto do tenant na requisição
        $request->attributes->set('tenant', $tenant);

        return $next($request);
    }
}

(3) Middleware CORS

PHP
// app/Http/Middleware/HandleCors.php
class HandleCors
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        $response->headers->set('Access-Control-Allow-Origin', config('cors.allowed_origins'));
        $response->headers->set('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
        $response->headers->set('Access-Control-Allow-Headers', 'Content-Type,Authorization,X-Tenant-Id');
        $response->headers->set('Access-Control-Max-Age', '86400');

        if ($request->isMethod('OPTIONS')) {
            $response->setStatusCode(204);
        }

        return $response;
    }
}

(1) ▶ Exemplo: Middleware de Limite de Requisições do ShopMetrics

PHP
// app/Http/Middleware/PerTenantRateLimit.php
class PerTenantRateLimit
{
    public function handle(Request $request, Closure $next, int $maxAttempts = 60, int $decayMinutes = 1): Response
    {
        $tenant = app()->make(Tenant::class);
        $key = 'tenant:' . $tenant->id . ':' . $request->ip();

        if (RateLimiter::tooManyAttempts($key, $maxAttempts)) {
            $seconds = RateLimiter::availableIn($key);
            abort(429, "Too many requests. Try again in {$seconds} seconds.");
        }

        RateLimiter::hit($key, $decayMinutes * 60);

        $response = $next($request);

        $response->headers->set('X-RateLimit-Limit', $maxAttempts);
        $response->headers->set('X-RateLimit-Remaining', $maxAttempts - RateLimiter::attempts($key));

        return $response;
    }
}

Saída:

TEXT
// Execução bem-sucedida

6. Parâmetros do Middleware

(1) Passagem de Parâmetros

PHP
// Definição de rota com parâmetros
Route::middleware('role:admin,super_admin')
    ->get('/admin/dashboard', [AdminController::class, 'dashboard']);

// Middleware recebe parâmetros
class CheckRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        if (!in_array($request->user()->role, $roles)) {
            abort(403, 'Insufficient role. Required: ' . implode(', ', $roles));
        }
        return $next($request);
    }
}

(2) O parâmetro throttle

PHP
// Throttle integrado com parâmetros
Route::middleware('throttle:60,1')->group(function () {
    // 60 requisições por 1 minuto
});

Route::middleware('throttle:10,1')->group(function () {
    // 10 requisições por 1 minuto (para endpoints custosos)
});

// Limitador de taxa personalizado
RateLimiter::for('api', function (Request $request) {
    return $request->user()
        ? Limit::perMinute(60)->by($request->user()->id)
        : Limit::perMinute(10)->by($request->ip());
});

RateLimiter::for('tenant-api', function (Request $request) {
    $tenant = app()->make(Tenant::class);
    $plan = $tenant->subscription?->plan;

    return Limit::perMinute(match ($plan->slug ?? 'starter') {
        'enterprise' => 300,
        'pro' => 120,
        default => 60,
    })->by($tenant->id);
});
Formato de Parâmetro Descrição Exemplo
middleware:p1 Parâmetro único role:admin
middleware:p1,p2 Múltiplos Parâmetros role:admin,editor
throttle:max,decay Parâmetros de Throttling throttle:60,1

(1) ▶ Exemplo: Limite de Requisições Baseado em Assinatura do ShopMetrics

PHP
// app/Providers/AppServiceProvider.php
public function boot(): void
{
    RateLimiter::for('tenant-api', function (Request $request) {
        $tenant = app()->make(Tenant::class);
        $plan = $tenant->plan ?? null;

        return Limit::perMinute(match ($plan->slug ?? 'starter') {
            'enterprise' => 600,
            'pro' => 120,
            default => 60,
        })->by($tenant->id)->response(function () {
            return response()->json([
                'message' => 'Rate limit exceeded. Upgrade your plan for higher limits.',
                'upgrade_url' => route('pricing'),
            ], 429);
        });
    });
}

// routes/api.php
Route::middleware(['auth:sanctum', 'tenant', 'throttle:tenant-api'])
    ->prefix('v1')
    ->group(function () {
        Route::apiResource('shops', Api\ShopController::class);
    });

Saída:

TEXT
// Execução bem-sucedida

7. Ordenação e Prioridade do Middleware

(1) Prioridade Padrão

O middleware é executado na ordem em que é registrado, mas certos middlewares devem ser executados primeiro (por exemplo, CORS deve ser processado antes da autenticação).

PHP
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
    $middleware->priority([
        \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TenantResolve::class,  // Deve executar após CORS
        \App\Http\Middleware\Authenticate::class,
    ]);
})

(2) Middleware terminable

PHP
// app/Http/Middleware/SendAnalyticsEvent.php
class SendAnalyticsEvent
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    // Executa APÓS a resposta ser enviada ao cliente
    public function terminate(Request $request, Response $response): void
    {
        // Registro de analytics sem bloqueio
        AnalyticsEvent::create([
            'tenant_id' => app()->make(Tenant::class)?->id,
            'path' => $request->path(),
            'method' => $request->method(),
            'status' => $response->getStatusCode(),
            'duration_ms' => defined('LARAVEL_START')
                ? round((microtime(true) - LARAVEL_START) * 1000)
                : null,
        ]);
    }
}
Abordagem de Ciclo de Vida Momento Propósito
handle() (antes) Quando uma requisição é recebida Autenticar, filtrar e modificar a requisição
handle() (depois) Quando a resposta é retornada Modificar os headers da resposta
terminate() Após enviar uma resposta Logs, Estatísticas, Limpeza

(1) ▶ Exemplo: Configuração de Prioridade de Middleware do ShopMetrics

PHP
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        // Prioridade — CORS primeiro, auth antes do tenant
        $middleware->priority([
            \Illuminate\Http\Middleware\HandleCors::class,
            \App\Http\Middleware\TenantResolve::class,
            \App\Http\Middleware\Authenticate::class,
            \App\Http\Middleware\CheckRole::class,
        ]);

        // Aliases
        $middleware->alias([
            'tenant' => \App\Http\Middleware\TenantResolve::class,
            'role' => \App\Http\Middleware\CheckRole::class,
            'tenant.throttle' => \App\Http\Middleware\PerTenantRateLimit::class,
        ]);

        // Adições ao grupo API
        $middleware->appendToGroup('api', [
            \App\Http\Middleware\EnsureJsonAccept::class,
        ]);
    });

Saída:

TEXT
// Execução bem-sucedida

8. Exemplo Abrangente: A Arquitetura de Middleware do ShopMetrics

PHP
// ============================================
// Abrangente: Stack de Middleware do ShopMetrics
// Cobre: global, rota, grupos, params, prioridade, terminable
// ============================================

// app/Http/Middleware/TenantResolve.php
class TenantResolve
{
    public function handle(Request $request, Closure $next): Response
    {
        $user = $request->user();
        if (!$user?->tenant_id) {
            abort(403, 'No tenant associated.');
        }
        $tenant = $user->tenant;
        if ($tenant->status !== 'active') {
            abort(403, 'Tenant is ' . $tenant->status);
        }
        app()->instance(Tenant::class, $tenant);
        return $next($request);
    }
}

// app/Http/Middleware/CheckRole.php
class CheckRole
{
    public function handle(Request $request, Closure $next, string ...$roles): Response
    {
        if (!in_array($request->user()?->role, $roles)) {
            abort(403, 'Required role: ' . implode('|', $roles));
        }
        return $next($request);
    }
}

// app/Http/Middleware/EnsureSubscriptionActive.php
class EnsureSubscriptionActive
{
    public function handle(Request $request, Closure $next): Response
    {
        $tenant = app()->make(Tenant::class);
        $subscription = $tenant->subscription;
        if (!$subscription || $subscription->status !== 'active') {
            abort(402, 'Active subscription required.');
        }
        return $next($request);
    }
}

// app/Http/Middleware/LogApiRequests.php
class LogApiRequests
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        ApiLog::create([
            'tenant_id' => app()->make(Tenant::class)?->id,
            'user_id' => $request->user()?->id,
            'method' => $request->method(),
            'path' => $request->path(),
            'status' => $response->getStatusCode(),
            'ip' => $request->ip(),
        ]);
    }
}

// Rotas usando o stack completo de middleware
Route::middleware(['auth:sanctum', 'tenant', 'throttle:tenant-api', 'subscription.active'])
    ->prefix('v1')->group(function () {
        Route::apiResource('shops', ShopController::class);
        Route::middleware('role:tenant_owner,analyst')
            ->apiResource('analytics', AnalyticsController::class)->only(['index', 'show']);
        Route::middleware('role:tenant_owner')
            ->apiResource('settings', SettingsController::class);
    });

❓ Perguntas Frequentes

P Qual é a diferença entre middleware de rota e middleware de construtor de controller?
R O middleware de rota é aplicado quando uma rota é correspondida, sendo mais flexível (pode ser personalizado por rota); o middleware de construtor de controller é executado quando o controller é instanciado, sendo adequado para todos os métodos dentro do controller. O middleware de rota deve ser usado sempre que possível.
P Quando o método terminate é executado?
R É executado após a resposta ser enviada ao cliente (similar a register_shutdown_function). É adequado para operações como logging e estatísticas que não precisam afetar a resposta. terminate não bloqueia o usuário.
P Quais problemas podem surgir se a ordem do middleware estiver incorreta?
R O middleware CORS deve ser executado antes da autenticação; caso contrário, a requisição de preflight (OPTIONS) será interceptada pelo middleware de autenticação e retornará um erro 401. TenantResolve deve ser executado antes do middleware de negócio; caso contrário, tenant() será null.
P Como faço um short-circuit de uma requisição em um middleware?
R Retorne um objeto Response diretamente sem chamar $next($request): return response()->json(['error' => 'Unauthorized'], 401);. O middleware e controllers subsequentes não serão executados.
P O middleware global pode excluir certas rotas?
R No Laravel 11, você pode usar $middleware->skipWhen(callback) ou $request->is('api/*') dentro do middleware para determinar se deve pular uma rota. Não é recomendado incluir verificações de rota no middleware global.
P Qual é a relação entre o RateLimiter e o middleware throttle?
R O middleware throttle usa o Facade RateLimiter internamente. Ao criar lógica personalizada de limite de requisições, primeiro defina a estratégia em RateLimiter::for(), depois referencie-a na rota usando throttle:nome_da_estratégia.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (⭐): Crie um middleware TenantResolve que resolva o tenant a partir de um usuário autenticado e o armazene em um container, depois use-o em um grupo de rotas de API. Teste que usuários sem um tenant vinculado recebem um erro 403 ao tentar acessar a API.

  2. Exercício Avançado (⭐⭐): Implemente o middleware PerTenantRateLimit para impor limite de requisições baseado no ID do tenant e endereço IP, definindo limites de taxa dinamicamente de acordo com o plano de assinatura (Starter: 60/min, Pro: 120/min, Enterprise: 600/min).

  3. Desafio (⭐⭐⭐): Desenvolva um sistema completo de prioridade de middleware—CORS → TenantResolve → Auth → SubscriptionActive → CheckRole → throttle—para garantir que cada middleware execute no momento correto. Escreva testes para verificar que requisições de pre-check OPTIONS não são interceptadas pela autenticação.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%