gdias 2 weken geleden
bovenliggende
commit
755538ec34
2 gewijzigde bestanden met toevoegingen van 55 en 1 verwijderingen
  1. 51 0
      middlewares/CorsMiddleware.php
  2. 4 1
      public/index.php

+ 51 - 0
middlewares/CorsMiddleware.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Middlewares;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Message\Response;
+use React\Promise\PromiseInterface;
+
+/**
+ * CORS global da API.
+ *
+ * Registrado como middleware GLOBAL (no construtor do App em public/index.php),
+ * portanto roda antes do roteamento. Isso é essencial para responder ao
+ * preflight `OPTIONS` — que não possui rota registrada e, sem este middleware,
+ * cairia em 404 antes do navegador concluir a requisição real.
+ *
+ * Política atual: liberar todas as origens (`*`). Como usamos "*", NÃO enviamos
+ * `Access-Control-Allow-Credentials` (o navegador rejeita a combinação) — a
+ * autenticação é via header `Authorization: Bearer`, não via cookie, então isso
+ * não é um problema.
+ */
+final class CorsMiddleware
+{
+    public function __invoke(ServerRequestInterface $request, callable $next)
+    {
+        // Preflight: responde imediatamente, sem chegar ao roteador/controllers.
+        if (strtoupper($request->getMethod()) === 'OPTIONS') {
+            return $this->withCorsHeaders(new Response(204));
+        }
+
+        $response = $next($request);
+
+        // Controllers respondem de forma síncrona (Response), mas o FrameworkX
+        // também aceita Promise. Tratamos ambos para anexar os headers de CORS.
+        if ($response instanceof PromiseInterface) {
+            return $response->then(fn (ResponseInterface $resolved): ResponseInterface => $this->withCorsHeaders($resolved));
+        }
+
+        return $this->withCorsHeaders($response);
+    }
+
+    private function withCorsHeaders(ResponseInterface $response): ResponseInterface
+    {
+        return $response
+            ->withHeader('Access-Control-Allow-Origin', '*')
+            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
+            ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
+            ->withHeader('Access-Control-Max-Age', '86400');
+    }
+}

+ 4 - 1
public/index.php

@@ -1,6 +1,7 @@
 <?php
 
 use FrameworkX\App;
+use Middlewares\CorsMiddleware;
 use Routes\Dispatcher;
 
 require __DIR__ . '/../vendor/autoload.php';
@@ -21,7 +22,9 @@ if (class_exists(Dotenv\Dotenv::class) && file_exists(__DIR__ . '/../.env')) {
 
 error_reporting(E_ALL);
 
-$app = new App();
+// CorsMiddleware é GLOBAL: roda antes do roteamento para também responder ao
+// preflight OPTIONS (que não possui rota e cairia em 404 sem este tratamento).
+$app = new App(new CorsMiddleware());
 
 // As rotas (com autenticação JWT e autorização por papel) ficam centralizadas
 // em routes/Dispatcher.php.