Какой правильный путь при написании роутинга?

Здравствуйте, в интернете видел много статей о том, как написать свой роутинг, во всех статьях всегда пишут, что они показывают самый простой и не всегда правильный вариант, что они просто хотят показать сам принцип работы... Принцип работы я понял: разбираем uri на части и в зависимости от того, что там содержится вызываем определённый метод нужного класса.

Хотелось бы, всё же, найти/научиться правильному варианту, может есть где-то уже инфа на русском? Книги, статьи и так далее, может у вас есть возможность тут описать?

Так же нашёл два принципиально разных подхода, привожу ссылки:
rmcreative.ru/blog/post/mvc-front-controller-contr...
https://habrahabr.ru/post/150267/#RouterCoding

Какой из этих подходов более правильный?
  • Вопрос задан
  • 1026 просмотров
Решения вопроса 2
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
Для начала давайте разберемся с тем что такое "правильно". Правильно в контексте вопроса - это максимально гибко. В приведенных вами ссылке "роутер" влияет на то, как будет устроена вся система. То есть в них мы сначала делаем допущение "у нас будут контроллеры, модели и представление, что бы как в рельсах но по другому).

Роутер же не должен ничегошеньки знать о контексте своего использования. Вот это будет правильно. Мы просто должы скормить ему какие-то правила маршрутизации, задающие соответствие "uri => mixed" к примеру, а так же иметь возможность скормить ему строчку что бы тот сказал кого все же вызывать.

Все, на этом зона ответственности "роутера" заканчивается. Грубо говоря самая примитивная реализация роутера:

// наш примитивный роутер
function getRoute(array $rules, $uri) {
     return $rules[$uri] ?? null;
}

// использование роутера
$action = getRoute([
    '/' => 'indexPage',
    '/about' => 'aboutPage',
    '/blog' => 'blogPage'
], '/about');

// больше нам уже роутер не нужен
// вызываем действие
call_user_func($app, $action);


Далее уже мы можем решать как нам будет удобнее организовать работу с правилами. То есть сразу мы видим что тут статическая карта маршрутов, а стало быть... мягко скажем не гибко. Тут мы можем использовать либо uriTemplates либо просто регулярные выражения либо придумать свою надстройку над регулярками для более удобного составления правил.

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

$rules = [
     [ 'uri' => '/blog/page{pageNumber:\d+}', 'method' => ['GET', 'HEAD'] ]
];


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

Запуском же конкретных действий по найденному маршруту пусть лучше занимается другой компонент.
Ответ написан
copist
@copist
Empower people to give
Не хотел бы углублять в конкретную реализацию. Объяснение на уровне абстракций.

C моей точки зрения роутер должен выполнять функцию определения по методу HTTP и строке URI, какая функция должна быть вызвана.

Результатом работы роутера может быть название класса и метода, как в Yii 1. Или может быть даже анонимная функция, как в Laravel, Phalcon, Django, RoR или ExpressJS. Это может быть, наверное, даже ассоциативный массив типа [ "response headers", "response body" ] или имя статического файла.

Как сказал Сергей Протько, гибкость роутера в том, что роутер ничего не должен знать о логической структуре приложения, но такое я могу сказать только о базовом универсальном роутинге, который работает по карте сопоставлений URI и программных методов. Он действительно мало что знает, потому что парсит регулярки, ищет максимально похожее выражение, возвращает какие-то абстракции.

С моей точки зрения, гибкий роутинг - это такой, который может быть легко заменён на любую альтернативную реализацию. Совместимость обеспечивается использованием объектно-ориентированного подхода с применением интерфейсов или через duck-typing.
Например, роутером может считаться класс, который реализует RoutingInterface (это согласно ООП) или содержит метод route(arguments) (это duck-typing).

Такой подход позволяет реализовать, например, сопоставление пути вида myblog/my-article с контролером
"blog/view" через поиск по базе данных, а не по регулярке.

И вот у Сергей Протько был роутинг с картой сопоставлений, а я заменил на роутинг с использованием базы данных. Логичный вопрос: делать карту сопоставления для всех роутов (что если правил будет больше 100?), либо всё это заносить в базу данных? (всё-всё-всё, каждый путь к профилю всех 100 000 пользователей - они же одинаковые /profile/111222). Ну не гибко же!

Ещё более гибкий роутинг позволяет реализовать каскад роутеров. Если один роутер не смог распознать, кто будет отвечать за этот URI, управление передаётся другому.

Таким образом можно сначала "натравить" на поиск по базе данных, затем по картам соответствий с регулярками.

Или разбить приложение на модули и реализовать роутинг в каждом модуле приложения, а затем соединять их каскадами. Например, пусть за роутинг профиля пользователя отвечает модуль "User", а за записи блога - модуль Blog. Тогда URI вида /profile/111222 возьмёт на себя User, а /profile-as-an-article мог бы бы перехватить блог, и уже если никто не смог, то 404 Page Not found.

Идея гибкого роутинга ясна? Нужны примеры с конкретной реализацией?
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@titronfan
Правильный путь по идее - это использовать то, что уже написали т.к. там уже по 1000 все продумали. Попробуйте поковырять простецкий фреймворк CodeIgniter. В Youtube есть обучающие материалы по нему.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы