16
Маршрутизация (Routing) в ASP.NET 4.0
Программирование
Tagged Under : ASP.NET, Routing, маршрутизация
Ниже представлена часть моего доклада на последнем семинаре в компании о маршрутизации и их применению при построении веб-приложений.
Итак, маршрутизация, появилась изначально в приложениях MVC, с версии ASP.NET 4.0 (собственно, можно и в 3.5 об этом рассказывается в одной из последних ссылок в конце) она стала доступна для WebForms – приложений. Для настройки маршрутизации необходимо подключить пространство имён System.Web.Routing.
Создание и настройка маршрутов происходит в файле Global.asax. Основные шаги заключаются в следующем:
- Подключаем пространство имён System.Web.Routing
- Создаём свой метод регистрации маршрутов, который в качестве параметра будет принимать коллекцию маршрутов, которая содержится в свойстве Routes класса RouteTable
- Создаем и добавляем маршруты с помощью метода MapPageRoute экземпляра класса RouteCollection
- Пользуемся маршрутизацией при построении ссылок
Рассмотрим пример создания маршрута для простейшего веб-приложения. Создадим проект ASP.NET Empty Web Application и добавим к проекту две aspx-страницы, а также файл Global.asax.
Далее в файле Global.asax подключаем пространство имён System.Web.Routing и создаем приватный метод RegisterRoutes (или какой-либо другой) принимающий в качестве параметра экземпляр класса RouteCollection и ничего не возвращающий.
Далее в нём пишем следующий код:
{
Route routeItem = routes.MapPageRoute("Default", "/Default/", "~/Default.aspx");
}
Итак, в приведенном выше коде, мы создаем и сразу добавляем маршрут в таблицу с помощью метода MapPageRoute, который в качестве параметров принимает следующее:
- Название маршрута, значение должно быть уникальным для приложения
- Желаемый маршрут
- Страница-обработчик
Необходимо отметить, что желаемый маршрут не должен начинаться на / и на ~, а также не должен содержать знак ?.
Теперь необходимо вызвать наш метод, передав ему коллекцию маршрутов из таблицы
{
RegisterRoutes(RouteTable.Routes);
}
Простейшее приложение использующее маршруты готово. Запускаем и пробуем ввести в адресную строку адреса Default.aspx и Default – приложение работает корректно в обоих случаях, что может вполне пригодится при переходе на Routing в уже созданных приложениях.
Теперь попробуем усложнить наш пример. Возьмём, например, раздел новости сайта dfm. Главная страница имеет адрес dfm.ru/press/news, которой соответствует страница news.aspx. На странице есть пейджинг, так что возможны адреса наподобие press/news/page-1 и мы должны позаботиться о том, чтобы можно было зайти внутрь новости имеющей адрес /press/news/movies или другой алиас/id.
Итак, создадим маршрут, а точнее маршруты.
routes.MapPageRoute("news with alias and paging", @"press/news/{alias}/{page}", "~/Default.aspx", false, new RouteValueDictionary { { "alias", "all" }, {"page", "page-1"} }, new RouteValueDictionary { {"alias", "all|movies"}, {"page", @"(page-d)?"} });
routes.MapPageRoute("current news item", "press/news/{alias}/{id}/", "~/Default.aspx", false, new RouteValueDictionary { { "alias", "all" }, { "id", 0 } }, new RouteValueDictionary { {"alias", "all|movies"}, {"id", @"d*"}});
Итак, в коде создано три маршрута:
- Главная страница новостей с пейджингом
- Страница раздела новостей с пейджингом
- Страница конкретной новости
Как видим метод MapPageRoute имеет перегрузку и может принимать дополнительные параметры
- Проверка на наличие физической страницы
- Словарь значений по умолчанию
- Словарь возможных значений
Ещё одна из перегрузок определяет четвертый параметр, который также является словарем и содержит заранее определённые данные, передаваемые на страницу в случае, когда мы попадаем на адрес, соответствующий маршруту.
Это можно использовать, чтобы как раз добиться результата аналогичного с работой маскарадинга для различных веб-приложений при передаче на страницу скрытых полей (не отображающихся в адресе страницы). Проделаем что-нибудь подобное, для этого модернизируем последний код следующим образом:
routes.MapPageRoute("news with alias and paging", @"press/news/{alias}/{page}", "~/Default.aspx", false, new RouteValueDictionary { { "alias", "all" }, { "page", "page-1" } }, new RouteValueDictionary { { "alias", "all|movies" }, { "page", @"(page-d)?" } }, new RouteValueDictionary { { "hidden_param", 998 } });
routes.MapPageRoute("current news item", "press/news/{alias}/{id}/{*optional}", "~/Default.aspx", false, new RouteValueDictionary { { "alias", "all" }, { "id", 0 } }, new RouteValueDictionary { { "alias", "all|movies" }, { "id", @"d*" } }, new RouteValueDictionary { { "hidden_param", 998 } });
Примечание: при построении маршрутов можно использовать в параметре routeUrl, который идёт вторым, строку вида {*какой-то параметр}, такие параметры называются catch all параметрами и ставятся последними. Также использовать их можно только один раз в routeUrl-параметре. Их назначение «забирать» всё то, что идёт в конце маршрута.
Итак, маршруты построены, но не рассмотренным остался вопрос получения всех необходимых данных на странице. Перейдём в метод Page_Load файла Default.aspx.cs и напишем следущий код
Дело в том, что на странице нам доступно свойство RouteData страницы, которое содержит все необходимые данные. Значение определённого параметра можно получить по ключу в виде строки, например, RouteData.Values["alias"] на выходе получаем объект. Те данные, что скрыты, можно получить через RouteData.DataTokens["hidden_param"] на выходе также объект.
Ну, и на последок, генерация ссылок осуществляется через метод GetRouteUrl, которому на вход оптимальнее всего передавать первым параметром имя маршрута, которое определено в файле Global.asax, а вторым параметром словарь параметров для маршрута, например alias, id, page и т.д..
На выходе получаем относительную ссылку /press/news/all/23/ можно, в принципе, эту же ссылку написать вручную – разницы нет.
Примечание: при переходе со стандартной адресации на адресацию через маршруты страницы сайта будут доступны и по новым и по старым адресам, но вот получение переданных параметров будет различно. В качестве решения от себя могу предложить написать расширяющий метод для HttpRequest, который будет доступен в экземпляре Request и который будет единственным интерфейсом получения данных по ключу. Что-то наподобие object Request.GetData(string key).
Примечание: для редиректа можно использовать Response.RedirectToRoute() , где в качестве параметра указать имя маршрута и/или нужные параметры.
Примечание: ещё одна интересная вещь, допустим если у вас есть конкретные адреса вида default.aspx, list.aspx и т.д. и хотелось бы получить на выходе default/ и list/, то вполне возможно написать общий маршрут вида
Если есть параметры, то вполне можно указать их в исходном адресе в {название параметра} и в конечном маршруте.
Подводя итог можно выделить следующие интересные моменты и преимущества:
- Маршруты достаточно гибко настраиваются и могут быть перенесены в базу
- Для каждого параметра можно указать допустимые значения (можно больше не опасаться нечто вроде калябаля в параметре, вместо этого будет сгенерирована 404, а не 500), т.е. избавляемся от лишних проверок в коде
- Если учесть, что это встроенная вещь, то есть надежда, что она работает быстрее надстроек
- Хорошо для SEO
- Можно обойтись без надстроек
- Это не редирект и не реврайтинг
- Возможность однозначной выполнения сценария на странице (например, когда страница обработчик работает с различными вариантами ключей, можно просто передать какой-то скрытый параметр и по нему на странице отработать сценарий)
Ссылки на материалы:







Костяныч, у тебя баг )
Route routeItem = routes.MapPageRoute(«Default», «/Default/», «~/Default.aspx»);
Во втором параметре лишние слеши )
сразу видно, что семинар прошел не даром. действительно со слеша начинатся этот параметр не может.
Спасибо за статью!
На вэбформах может вылезти баг, если сделать routes.MapPageRoute(«test», «{page}/», «~/{page}.aspx»);
Перестанет находить webresource.axd, ибо будет мапить его на webresource.axd.aspx
Лечится так:
routes.Ignore(«{resource}.axd/{*pathInfo}»);
routes.MapPageRoute(«allpages», «{page}/», «~/{page}.aspx»);