miladrahimi / phprouter

PhpRouter is a full-featured yet very fast HTTP URL router for PHP projects
MIT License
195 stars 17 forks source link

Distinguish between HTTP 404 and 405 #51

Open jonaskohl opened 1 year ago

jonaskohl commented 1 year ago

Currently the router throws a RouteNotFoundException either if it doesn't find a route with a matching pattern or a matching method. My suggestion is to introduce either a property on RouteNotFoundException or a new MethodMismatchException to differentiate between a 404 Not Found and a 405 Method Not Allowed error.

Example:

<?php

use MiladRahimi\PhpRouter\Router;
use Laminas\Diactoros\Response\HtmlResponse;
use MiladRahimi\PhpRouter\Exceptions\RouteNotFoundException;

require __DIR__ . "/vendor/autoload.php";

$router = Router::create();
$router->get('/', function() use ($twig) {
    return new HtmlResponse("<h1>Test</h1>\n");
});

try {
    $router->dispatch();
} catch (RouteNotFoundException $ex) {
    // We have no way to tell if it really is a 404 or 405 error...
    $router->getPublisher()->publish(new HtmlResponse('Not found.', 404));
} catch (\Throwable $e) {
    $router->getPublisher()->publish(new HtmlResponse('Internal error.', 500));
}

If we POST http://<DOMAIN>/ we get a HTTP/1.1 404 Not Found, despite it actually resembeling a 405 error.

Proposal:

<?php

use MiladRahimi\PhpRouter\Router;
use Laminas\Diactoros\Response\HtmlResponse;
use MiladRahimi\PhpRouter\Exceptions\RouteNotFoundException;
use MiladRahimi\PhpRouter\Exceptions\MethodMismatchException;

require __DIR__ . "/vendor/autoload.php";

$router = Router::create();
$router->get('/', function() use ($twig) {
    return new HtmlResponse("<h1>Test</h1>\n");
});

try {
    $router->dispatch();
} catch (RouteNotFoundException $ex) {
    $router->getPublisher()->publish(new HtmlResponse('Not found.', 404));
} catch (MethodMismatchException $ex) {
    $router->getPublisher()->publish(new HtmlResponse('Cannot ' . htmlentities($_SERVER["REQUEST_METHOD"]) . " " . htmlentities($_SERVER["REQUEST_URI"]), 405));
} catch (\Throwable $e) {
    $router->getPublisher()->publish(new HtmlResponse('Internal error.', 500));
}

If we now POST http://<DOMAIN>/ we correctly get a HTTP/1.1 405 Method Not Allowed.