Как работать с 404 с помощью маршрутизации на основе Regex? - PullRequest
4 голосов
/ 14 июля 2010

Пожалуйста, рассмотрите следующие очень элементарные «контроллеры» (функции в данном случае для простоты):

function Index() {
    var_dump(__FUNCTION__); // show the "Index" page
}

function Send($n) {
    var_dump(__FUNCTION__, func_get_args()); // placeholder controller
}

function Receive($n) {
    var_dump(__FUNCTION__, func_get_args()); // placeholder controller
}

function Not_Found() {
    var_dump(__FUNCTION__); // show a "404 - Not Found" page
}

И следующая основанная на регулярных выражениях Route() функция :

function Route($route, $function = null)
{
    $result = rtrim(preg_replace('~/+~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))), '/');

    if (preg_match('~' . rtrim(str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $route), '/') . '$~i', $result, $matches) > 0)
    {
        exit(call_user_func_array($function, array_slice($matches, 1)));
    }

    return false;
}

Теперь я хочу сопоставить следующие URL-адреса (конечные слэши игнорируются) с соответствующими «контроллерами»:

/index.php -> Index()
/index.php/send/:NUM -> Send()
/index.php/receive/:NUM -> Receive()
/index.php/NON_EXISTENT -> Not_Found()

Это та часть, где все становится сложнее, яУ меня две проблемы, которые я не могу решить ... Я полагаю, что я не первый, у кого есть эта проблема, поэтому кто-то там должен иметь решение.


Поймать 404 (Решено!)

Я не могу найти способ различить запросы к корню (index.php) и запросы, которые не должны существовать, например (index.php/notHere).Я заканчиваю тем, что обслуживаю маршрут по умолчанию index.php для URL, которые в противном случае должны обслуживаться на странице ошибки 404 - Not Found. Как я могу решить эту проблему?

РЕДАКТИРОВАТЬ - Решение просто мелькнуло у меня в голове:

Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route('/:any', 'Not_Found'); // use :any here, see the problem bellow
Route('/', 'Index');

Заказ маршрутов

Если я настрою маршруты в «логическом» порядке, например:

Route('/', 'Index');
Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route(':any', 'Not_Found');

Все URL-запросы отлавливаются контроллером Index(), начиная с пустого регулярного выражения (помните: завершающие косые черты игнорируются ) соответствует всему.Однако, если я определяю маршруты в «хакерском» порядке, например:

Route('/send/(:num)', 'Send');
Route('/receive/(:num)', 'Receive');
Route('/:any', 'Not_Found');
Route('/', 'Index');

Все кажется работать так, как должно. Существует ли элегантный способ решения этой проблемы?

Маршруты не всегда могут быть жестко запрограммированы (извлечены из БД или чего-то в этом роде), и мне нужно убедиться, что он победил 'не игнорировать любые маршруты из-за порядка, в котором они были определены. Любая помощь приветствуется!

Ответы [ 3 ]

1 голос
/ 28 июля 2010

Это общая проблема с веб-приложениями MVC, которая часто решается до того, как она вообще становится проблемой.

Самый простой и общий способ - использовать исключения.Бросьте исключение PageNotFound, если у вас нет содержимого для заданных параметров.На верхнем уровне вашего приложения перехватите все исключения, как в этом упрощенном примере:

index.php:

try {
    $controller->method($arg);
} catch (PageNotFound $e) {
    show404Page($e->getMessage());
} catch (Exception $e) {
    logFatalError($e->getMessage());
    show500Page();
}

controller.php:

function method($arg) {
    $obj = findByID($arg);
    if (false === $obj) {
         throw new PageNotFound($arg);
    } else {
         ...
    }
}

Проблема упорядочения может быть решена путем сортировки регулярных выражений так, чтобы наиболее конкретное регулярное выражение совпадало первым, а наименее определенное совпадало последним.Для этого подсчитайте разделители пути (то есть косые черты) в регулярном выражении, за исключением разделителя пути в начале.Вы получите это:

 Regex           Separators
 --------------------------
 /send/(:num)    1
 /send/8/(:num)  2
 /               0

Сортируйте их по убыванию и обработайте.Порядок обработки:

  1. / send / 8 / (: num)
  2. / send / (: num)
  3. /
1 голос
/ 14 июля 2010

Хорошо, я знаю, что есть несколько способов снять шкуру с кошки, но почему в мире вы так делаете?Похоже, какой-то подход RoR к чему-то, что может быть легко обработано с помощью mod_rewrite

При этом я переписал вашу функцию Route и смог достичь вашей цели.Имейте в виду, что я добавил еще одно условие для непосредственного отслеживания индекса, когда вы убирали все /, и поэтому он соответствовал индексу, когда вы хотели, чтобы он совпадал с 404. Я также объединил 4 вызова Route () для использованияforeach ().

function Route()
{
        $result = rtrim(preg_replace('~/+~', '/', substr($_SERVER['PHP_SELF'], strlen($_SERVER['SCRIPT_NAME']))), '/');
        $matches = array();

        $routes = array(
                'Send'      => '/send/(:num)',
                'Receive'   => '/receive/(:num)',
                'Index'     => '/',
                'Not_Found' => null
        );

        foreach ($routes as $function => $route)
        {
                if (($route == '/' && $result == '')
                        || (preg_match('~' . rtrim(str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $route)) . '$~i', $result, $matches) > 0))
                {
                        exit(call_user_func_array($function, array_slice($matches, 1)));
                }
        }

        return false;
}

Route();

Ура!

0 голосов
/ 28 июля 2010

OK, во-первых, что-то вроде:

foo.com/index.php/more/info/to/follow 

совершенно корректно и по стандарту должно загружать index.php с $ _SERVER [PATH_INFO], установленным в / more / info / to / follow.Это CGI / 1.1 стандарт .Если вы хотите, чтобы сервер НЕ выполнял расширения PATH_INFO, отключите его в настройках вашего сервера.Под apache это делается с помощью:

AcceptPathInfo Off

Если вы установите его на Off в Apache2 ... Он отправит 404.

Я не уверен, что флаг IIS, ноЯ думаю, что вы можете найти это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...