Что такое роутинг в программировании
Перейти к содержимому

Что такое роутинг в программировании

  • автор:

Пишем современный маршрутизатор на JavaScript

Простые одностраничные приложения, основанные на React, Vue или чистом JavaScript, окружают нас повсюду. Хороший «одностраничник» предполагает соответствующий механизм маршрутизации.

Такие библиотеки, как «navigo» или «react-router», приносят большую пользу. Но как они работают? Необходимо ли нам импортировать всю библиотеку? Или достаточно какой-то части, скажем, 10%? В действительности, быстрый и полезный маршрутизатор можно легко написать самому, это займет немного времени, а программа будет состоять менее чем из 100 строчек кода.

Требования

Наш маршрутизатор должен быть:

  • написан на ES6+
  • совместим с историей и хешем
  • переиспользуемой библиотекой
  • маршрутизаторы (routes): список зарегистрированных маршрутизаторов
  • режим (mode): хеш или история
  • корневой элемент (root): корневой элемент приложения, если мы находимся в режиме использования истории
  • конструктор (constructor): основная функция для создания нового экземпляра маршрутизатора
class Router < routes = [] mode = null root = '/' constructor(options) < this.mode = window.history.pushState ? 'history' : 'hash' if (options.mode) this.mode = options.mode if (options.root) this.root = options.root >> export default Router 

Добавление и удаление маршрутизаторов

Добавление и удаление маршрутизаторов осуществляется через добавление и удаление элементов массива:

class Router < routes = [] mode = null root = '/' constructor(options) < this.mode = window.history.pushState ? 'history' : 'hash' if (options.mode) this.mode = options.mode if (options.root) this.root = options.root >add = (path, cb) => < this.routes.push(< path, cb >) return this > remove = path => < for (let i = 0; i < this.routes.length; i += 1) < if (this.routes[i].path === path) < this.routes.slice(i, 1) return this >> return this > flush = () => < this.routes = [] return this >> export default Router 

Получение текущего пути

Мы должны знать, где находимся в приложении в определенный момент времени.

Для этого нам потребуется обработка обоих режимов (истории и хеша). В первом случае, нам нужно удалить путь к корневому элементу из window.location, во втором — «#». Нам также необходима функция (clearSlash) для удаления всех маршрутизаторов (строки от начала до конца):

[. ] clearSlashes = path => path .toString() .replace(/\/$/, '') .replace(/^\//, '') getFragment = () => < let fragment = '' if (this.mode === 'history') < fragment = this.clearSlashes(decodeURI(window.location.pathname + window.location.search)) fragment = fragment.replace(/\?(.*)$/, '') fragment = this.root !== '/' ? fragment.replace(this.root, '') : fragment >else < const match = window.location.href.match(/#(.*)$/) fragment = match ? match[1] : '' >return this.clearSlashes(fragment) > > export default Router 

Навигация

Ок, у нас имеется API для добавления и удаления URL. Также у нас имеется возможность получать текущий адрес. Следующий шаг — навигация по маршрутизатору. Работаем со свойством «mode»:

[. ] getFragment = () => < let fragment = '' if (this.mode === 'history') < fragment = this.clearSlashes(decodeURI(window.location.pathname + window.location.search)) fragment = fragment.replace(/\?(.*)$/, '') fragment = this.root !== '/' ? fragment.replace(this.root, '') : fragment >else < const match = window.location.href.match(/#(.*)$/) fragment = match ? match[1] : '' >return this.clearSlashes(fragment) > navigate = (path = '') => < if (this.mode === 'history') < window.history.pushState(null, null, this.root + this.clearSlashes(path)) >else < window.location.href = `$#$` > return this > > export default Router 

Наблюдаем за изменениями

Теперь нам нужна логика для отслеживания изменений адреса как с помощью ссылки, так и с помощью созданного нами метода «navigate». Также нам необходимо обеспечить рендеринг правильной страницы при первом посещении. Мы могли бы использовать состояние приложения для регистрации изменений, однако в целях изучения сделаем это с помощью setInterval:

class Router < routes = []; mode = null; root = "/"; constructor(options) < this.mode = window.history.pushState ? "history" : "hash"; if (options.mode) this.mode = options.mode; if (options.root) this.root = options.root; this.listen(); >[. ] listen = () => < clearInterval(this.interval) this.interval = setInterval(this.interval, 50) >interval = () => < if (this.current === this.getFragment()) return this.current = this.getFragment() this.routes.some(route =>< const match = this.current.match(route.path) if (match) < match.shift() route.cb.apply(<>, match) return match > return false >) > > export default Router 

Заключение

Наша библиотека готова к использованию. Она состоит всего лишь из 84 строчек кода!

Код и пример использования на Github.

Маршруты¶

../../_images/routes4.jpg

Идея маршрута (англ. — route) впервые появилась в Ruby on Rails и быстро обрела популярность в других веб-фреймворках. Также концептуально очень близкой системой является URLConf в Django. В последующих разработках наиболее мощной реализацией данной идеи, вероятно, является http://routes.groovie.org/, используемая в Pylons.

Маршруты отвечают за ключевую проблему веб-разработки: сопоставление кода и URL. Например, какой код должен отвечать за обработку запросов по адресу «/2008/01/08» или «/login» ? Во многих фреймворках используется фиксированная система диспетчеризации, например «/A/B/C» означает прочитать файл «C» в каталоге «B» (например /auth/login.php или /cgi-bin/hello.cgi ), или вызвать метод «С» класса «B» в модуле «A».

Это работает прекрасно до тех пор, пока не возникает необходимости в реорганизации кода, и выясняется, что закладки пользователей стали недействительны. Кроме того, если вы хотите переделать адреса (например, создать раздел в подразделе), то нужно изменить уже отлаженную логику по генерации ссылок внутри сайта.

Маршруты предлагают иной подход. Вы определяете шаблоны URL и связываете их со своим кодом. Если вы измените свое решение по поводу конкретного URL , то просто поменяйте шаблон URL — код по-прежнему будет работать отлично, и не понадобится менять какую-либо логику.

Сопоставление с образом¶

  • Сопоставление с образом
  • Python -> Selector

Регулярные выражения дают огромные возможности для обработки URL-путей, но из-за ограничений, описанных в стандарте RFC 1738, большинство из них не нужны, при этом использование регулярных выражений затрудняет читабельность кода. Более современный подход придуманный Ruby on Rails это использовать технологию сопоставление с образом . Рассмотрим отличия на примере нашего блога:

Исходный код доступен по адресу:

Примеры работают только в Python3

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
from views import BlogRead, BlogIndex, BlogCreate, BlogDelete, BlogUpdate from wsgi_basic_auth import BasicAuth # third-party import selector  def make_wsgi_app(): passwd =  'admin': '123' > # BasicAuth applications create = BasicAuth(BlogCreate, 'www', passwd) update = BasicAuth(BlogUpdate, 'www', passwd) delete = BasicAuth(BlogDelete, 'www', passwd) # URL dispatching middleware dispatch = selector.Selector() dispatch.add('/', GET=BlogIndex) dispatch.prefix = '/article' dispatch.add('/add', GET=create, POST=create) dispatch.add('/', GET=BlogRead)  dispatch.add('//edit', GET=update, POST=update)  dispatch.add('//delete', GET=delete)  return dispatch if __name__ == '__main__': from paste.httpserver import serve app = make_wsgi_app() serve(app, host='0.0.0.0', port=8000)
Сравнение URL ¶

Регулярные выражения Сопоставление с образом
/ /
/article/add /article/add
^/article/(?Pd+)/$ /article/
^/article/(?Pd+)/edit$ /article//edit
^/article/(?Pd+)/delete$ /article//delete

Previous: Разделение кода Next: Шаблоны

© Copyright 2020, Кафедра Интеллектуальных Информационных Технологий ИнФО УрФУ. Created using Sphinx 1.7.6.

PHP-роутинг (Routing) для новичков

Роутинг — это маршрутизация: входящий URL разбирается специальным образом и по его результату выполняется определенный код. С роутингом напрямую связано понятие ЧПУ (человекопонятные урлы), которое позволяет исключить в адресах сложные параметры. Например вместо http://сайт/admin/new-page пришлось бы использовать http://сайт/admin.php?action=new-page

Любой входящий URL на сервере разбирается по единому стандарту. Полностью приводить документацию не буду (см. как пример функцию parse_url), важно лишь понять, что в адресе передается параметр path (путь на сервере), которого на сервере реально может не быть. Например в адресе http://сайт/admin каталога admin реально может не существовать.

То есть сервер, получив такой адрес, попытается найти каталог admin , но не найдя его, выдаст 404-страницу (not found).

Чтобы исключить такой вариант, серверу указывается, что для всех несуществующих каталогов и файлов, подключать php-файл (обычно index.php ).

Делается это в файле .htaccess с помощью Apache-модуля mod_rewrite. Вот довольно типовой вариант (MaxSite CMS):

RewriteCond % !-f RewriteCond % !-d RewriteRule ^(.*)$ /index.php/$1 [L,QSA]

Тут главная строчка с RewriteRule — именно она определяет шаблон входящего адреса (в примере это регулярное выражение) и что с ним делать. В данном примере будет подключен index.php с параметрами после слэша.

Строчка RewriteCond % !-f / -d указывает исключить из обработки реально существующие на сервере файлы и каталоги.

Похожий вариант, только чуть короче, от WordPress:

RewriteRule . /index.php [L]
RewriteRule ^(.*)$ /index.php?page=$1 [QSA]

Здесь принудительно добавляется query-параметр page.

Еще один распространенный вариант (пожалуй самый «типовой»):

RewriteRule (.*) index.php?$1 [QSA,L]

Все эти RewriteRule-правила делают простую вещь: как бы «преобразуют» входящий адрес в набор query-параметров. Например адрес http://сайт/admin превратится в http://сайт/index.php?admin

Посмотрите на полный код .htaccess :

 RewriteEngine on RewriteBase / RewriteCond % !-f RewriteCond % !-d RewriteRule (.*) /index.php?$1 [QSA,L] 

Если это какой-то подкаталог, то он указываетс в RewriteBase и как путь к php-файлу. Например каталог на сервере route :

 RewriteEngine on RewriteBase /route/ RewriteCond % !-f RewriteCond % !-d RewriteRule (.*) /route/index.php?$1 [QSA,L] 

Теперь, все адреса на сервере будут передаваться в файл index.php , а исходный адрес сохранится в виде query-параметра.

Если в index.php разместить

То мы можем увидеть query-параметры. В PHP за это отвечается суперглобальная переменная $_GET . Например для http://сайт/admin это будет admin , для http://сайт/admin/new-page — admin/new-page .

Таким образом, с помощью .htaccess происходит первая часть роутинга, где мы получаем готовый $_GET .

Кстати, насчет .htaccess WordPress. Он не создает $_GET , поэтому придется использовать $_SERVER[‘REQUEST_URI’] в который включается подкаталог. Работать с таким адресом уже будет сложней.

Второй этап роутинга выполняется полностью на PHP. Получив $_GET нужно решить что с ним делать. Например если адрес admin, подключить файл admin.php .

Существуют несколько принципиально разных подходов в организации роутинга. Наиболее популярный подход — это когда в адресе передаётся «действие», которое описывается через php-класс. Такой подход хорошо описан в CodeIgniter:

example.com/class/function/id/

Например пусть будет класс admin в нём метод edit, принимающий параметр $id.

example.com/admin/edit/23 class admin < function edit($id) < . >>

Это сильно утрированный пример, но он хорошо показывает соответствие адреса и php-класса.

Другой вариант похожий, но используется не классы, а функции.

example.com/admin/edit/23 function admin($params)

То есть функция — это первый сегмент, а остальные выступают уже как парметры. Встречается более «продвинутый» вариант.

example.com/admin function admin($params) < . >example.com/admin/edit/23 function admin_edit($params)

То есть имя функции строится по сегментам URL.

Третий, тоже распространенный вариант — адрес указывает на подключаемый файл.

example.com/admin $fn = 'pages/admin.php'; if (file_exists($fn)) reqiure($fn); else reqiure('pages/404.php');

Здесь все файлы хранятся в каталоге pages и подключаются только если реально существуют. Если файла нет, то подключается предопределенный 404-файл.

Если сайт представляет собой сложный проект, то как правило используют роутинг на основе php-классов. Так его проще поддерживать и развивать. Если же сайт состоит из небольшого количества страниц, то как правило используют подключаемые файлы, где один файл — это одна страница.

Строго говоря, роутинг «на классах» тоже использует «файловое» подключение. Вначале подключается файл с кодом класса, а уже после этого выполняется сам класс.

В задачу роутинга входит не только необходимое «действие», но и валидация входящего адреса и его лексический разбор.

Адреса могут строиться по шаблону. Например какой-то адрес должен содержать только номер, а не текст (например example.com/admin/edit/23 , но не example.com/admin/edit/hello ). Или адрес может быть неизменным, но обработчик будет меняться от вида запроса GET или POST. В одном случае нужно подключить одну функцию/файл, в другом — другой. Бывают и более сложные задачи, все их перечислять нет смысла, поэтому появились разного рода php-библиотеки для организации роутинга.

Свой «велосипед» не изобретал только ленивый, но я отмечу довольно известный FastRoute, который вобрал в себя наиболее типичные решения.

В первую очередь это использование регулярных выражений при задании правил, например:

$r->addRoute('GET', '/user/', 'handler1');

Примерно такой же подход используется и в роутинге CodeIgniter.

products/shirts/123 $route['products/([a-z]+)/(d+)'] = "$1/id_$2";

То есть входящий адрес должен соответствовать шаблону и только в этом случае он «сработает».

В FastRoute реализована поддержка POST и GET-запросов. Такая возможность интересна, хотя на больших проектах такие вещи лучше делать на уровне самого «действия». Но это уже тонкости. Про эту библиотеку я упоминаю в первую очередь из-за того, что она достаточно популярна и уже используется в нескольких интересных проектах: Slim и Lumen.

Для небольших проектов, конечно же, FastRoute будет избыточна, поэтому можно ограничиться вариантом попроще, да и мозги потренировать. 🙂

ТЗ. Пусть роутинг будет обрабатывать адреса вида http://сайт/blog/contact/map , по которому будет подключен файл content/blog/contact/map/index.php , где каталог content — это общее хранилище всех страниц сайта, а index.php — обязательный файл с кодом страницы. Роутинг должен проверять реальный файл и если его нет, то подключать 404-страницу. Если никаких параметров нет, то это home-страница. Пусть они будут предопределены.

Решение. По сути задача сводится к преобразованию входящего адреса в путь на сервере. У нас есть массив $_GET , где первый ключ и есть входящий URL. Дальше формируем путь к файлу и если он есть, то подключаем. Если нет, то подключаем 404-страницу.

Весь код в 2 строчки:

Это простой роутинг для простых проектов. Достаточно создать в content каталог, как он станет доступен по одноименному адресу.

Что такое роутинг или маршрутизация простыми словами для чайников

Lorem ipsum dolor

Напишем

Маршрутизатор или роутер — это аппаратное средство, которое обрабатывает отправляемые или получаемые потоки данных. В роли маршрутизаторов часто выступают отдельные устройства , что обеспечивает максимально быструю работу с данными , или обычные компьютеры с правильно настроенным специализированным программным обеспечением.

Роутинг — это сложный механизм передачи данных

Напишем

Роутинг бывает разный, например, различают два основных вида роутинга:

  1. Прямой роутинг — это когда данные могут передаваться внутри одной сети, минуя IP-маршрутизацию. При таком подходе перед отправкой данных узел отправителя проверяет, находится ли получатель с ним в одной сети. И если это так, тогда отправитель отправляет на адрес получателя необходимый пакет данных. Для «определения адреса» в таком подходе есть даже собственный протокол ARP (Address Resolution Protocol).
  2. Косвенный роутинг — это когда пакеты с данными передаются между разными IP-сетями. В этом случае при передаче пакетов есть «посредник», он же маршрутизатор, он же роутер. При таком подходе отправитель передает пакет с данными маршрутизатору, а тот уже доставляет данные по нужному адресу.

Что должен выяснить роутер, чтобы отправить данные:

  1. Как правило, к одному роутеру подключа е т ся несколько различных интерфейсов разных сетей. Поэтому роутеру в первую очередь необходимо определить , в какой интерфейс отправлять пакет данных.
  2. Следующим шагом роутер должен выяснить, что конкретно нужно сделать с данными. Тут у роутера есть 2 решения: либо он передает пакет данных сразу в сеть, либо он передает данные другому маршрутизатору в этой сети. Когда он передает данные другому роутеру, то ему нужно точно знать , какому именно передать , ч тобы именно к передаваемому роутеру была подключена сеть с получателем.

Иногда так происходит , что на роутер приходит пакет с данными, для которого не ясен маршрут и не ясен получатель. В этом случа е маршрутизатор просто «выбрасывает» пакет с данными в сеть, тем самым «засоряя» сеть «мусорными пакетами».

Таблицы роутинга

  1. Адрес шлюза — это адрес самого роутера и других роутеров, на которые отправляются пакеты с данными.
  2. Интерфейс — это физические порты, по которым осуществляется движение пакетов.
  3. Метрику — числовое значение, определяющее приоритет маршрута.
  4. Маску подсети — это битовое значение, которое помогает определить по заданному IP-адресу адреса отдельных узлов подсети и адрес самой подсети.
  5. Сетевой адрес — это ID устройства, подключенного к общей сети.

Как записываются данные в таблицу?

Напишем

Этот вопрос можно перефразировать так: «Как и кем составляются маршруты при передач е данных?». Маршрут может задаваться 3-мя способами:

  1. Роутер сам прописывает маршрут передачи и осуществляет записи в таблицу. Такой способ применим по «прямому маршруту», когда передача данных осуществляется внутри одной сети.
  2. Маршруты можно прописать «вручную». При таком подходе прописывается адрес следующего соседнего роу те ра, которому передаются пакеты данных, а он уже распределяет их по подключенным к нему сетям.
  3. Маршруты прописываются автоматически, используя протоколы маршрутизации. Данные протоколы самостоятельно отслеживают изменения в компоновке сети и вносят соответствующие коррективы в таблицу маршрутов.

Как рассчитывается маршрут роутинга

Напишем

Между получателем и отправителем может быть организовано несколько маршрутов передачи пакетов информации. Чтобы решить , по какому маршруту передать информацию, роутер проводит расчет лучшего маршрута, сравнивая их метрики, и вносит в таблицу маршрутов самый оптимальный из доступных.

При расчете метрик маршрута бер у тся во внимание:

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

Сравнивая все описанные свойства маршрута, роутер задает этому маршруту определенное числовое значение. Данное числовое значение принадлежит только этому маршруту и вносится в таблицу маршрутов в графу «Метрика». И если для передачи данных буд е т доступно несколько маршрутов, то роутер выберет тот, у которого будет меньшее число метрики.

Заключение

Роутинг — это неосязаемый процесс, который виден , только если его специально просмотреть. Даже сейчас, читая нашу статью, вы просто не замечаете , сколько незримых процессов происходит, а их происходит очень много.

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *