From fa663a6a6c3a32f7186adeed7a5fff047de2d52e Mon Sep 17 00:00:00 2001 From: Pier-Paolo Mammi Date: Thu, 16 Jul 2026 12:17:47 +0200 Subject: [PATCH] fix api routing and config paths --- .agents/AGENTS.md | 6 +++++- public/index.php | 6 +++--- src/Api/Controllers/V1/UsersController.php | 2 +- src/Api/Core/Config.php | 4 ++-- src/Api/Core/Router.php | 15 +++++++++++---- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index 4498799..e766a2c 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -7,7 +7,7 @@ Queste regole definiscono il comportamento per tutti gli sviluppi futuri su ques Tutte le dipendenze devono essere iniettate tramite costruttore. Il container in `bootstrap.php` provvederà all'autowiring automatico. 2. **Namespace e Struttura dei Controller** - Tutti i nuovi Controller devono essere posizionati all'interno della cartella relativa alla loro versione (es. `src/Controllers/V1/`) e devono avere il namespace corretto (es. `namespace Controllers\V1;`). Questo permette al `Router` di mapparli automaticamente partendo dalle route versionate (es. `/v1/risorsa`). + Tutti i nuovi Controller devono essere posizionati all'interno della cartella relativa alla loro versione (es. `src/Api/Controllers/V1/`) e devono avere il namespace corretto (es. `namespace Api\Controllers\V1;`). Questo permette al `Router` di mapparli automaticamente partendo dalle route versionate (es. `/v1/risorsa`). 3. **Risposte HTTP Standard** Non utilizzare mai funzioni di output diretto (come `echo`, `print` o `header()`) all'interno dei Controller. @@ -19,3 +19,7 @@ Queste regole definiscono il comportamento per tutti gli sviluppi futuri su ques 5. **Validazione dell'Input** Assicurati sempre di validare l'input proveniente da `$req->body()` o dai parametri URL prima di processarlo con la business logic applicativa. (Consigliato l'uso di DTO). + +6. **Routing API e prefissi di versione** + Le route devono essere registrate senza il prefisso `/api` e senza il prefisso di versione (es. usare `/users` invece di `/api/users` o `/api/v1/users`). Il router deve normalizzare i percorsi in ingresso rimuovendo il prefisso `/api` prima di confrontarli con le route registrate, così richieste come `/api/users` e `/api/v1/users` continuano a funzionare. + diff --git a/public/index.php b/public/index.php index 7965d5f..8512352 100644 --- a/public/index.php +++ b/public/index.php @@ -44,9 +44,9 @@ if (strpos($request->path(), '/api/') === 0) { } // register routes (path without version prefix) -$router->get('/users', 'UsersController@index'); -$router->post('/users', 'UsersController@create'); -$router->get('/example', 'ExampleController@test'); +$router->get('/users/index', 'UsersController@index'); +$router->post('/users/create', 'UsersController@create'); +$router->get('/example/test', 'ExampleController@test'); // Rate limiting by IP address $key = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; diff --git a/src/Api/Controllers/V1/UsersController.php b/src/Api/Controllers/V1/UsersController.php index b8493ac..d84bb9c 100644 --- a/src/Api/Controllers/V1/UsersController.php +++ b/src/Api/Controllers/V1/UsersController.php @@ -1,5 +1,5 @@ config === null) { - $file = __DIR__ . '/../../config/config.php'; + $file = __DIR__ . '/../../../config/config.php'; $this->config = file_exists($file) ? require $file : []; } } private function loadSecrets() { if ($this->secrets === null) { - $file = __DIR__ . '/../../config/secrets.php'; + $file = __DIR__ . '/../../../config/secrets.php'; $this->secrets = file_exists($file) ? require $file : []; } } diff --git a/src/Api/Core/Router.php b/src/Api/Core/Router.php index a4087d5..a316d3b 100644 --- a/src/Api/Core/Router.php +++ b/src/Api/Core/Router.php @@ -16,9 +16,17 @@ class Router { public function get($path, $handler) { $this->register('GET', $path, $handler); } public function post($path, $handler) { $this->register('POST', $path, $handler); } + private function normalizePath(string $path): string { + $path = '/' . trim($path, '/'); + $path = preg_replace('#^/api(?=/|$)#i', '', $path); + $path = '/' . trim($path, '/'); + + return $path === '' ? '/' : $path; + } + public function dispatch(Request $req, Response $res) { $method = $req->method(); - $path = $req->path(); + $path = $this->normalizePath($req->path()); // extract version prefix /v1/... if (preg_match('#^/v([0-9]+)(/.*)?$#', $path, $m)) { @@ -39,9 +47,9 @@ class Router { // handler can be 'UsersController@index' or 'Controllers\\UsersController@index' if (strpos($handler, '@') !== false) { list($class, $function) = explode('@', $handler); - // if class not namespaced, prefix with Controllers\V{n}\ + // if class not namespaced, prefix with Api\\Controllers\\V{n}\\ if (strpos($class, '\\') === false) { - $class = "Controllers\\V{$version}\\" . $class; + $class = "Api\\Controllers\\V{$version}\\" . $class; } } else { return $res->json(['error' => 'Invalid handler'], 500); @@ -51,7 +59,6 @@ class Router { return $res->json(['error' => 'Controller not found'], 500); } - // snippet inside Router::dispatch $controller = $this->container->make($class); return $controller->$function($req, $res); }