Маршрутизация (Routing) в ASP.NET 4.0

Программирование

Tagged Under : , ,

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

Итак, маршрутизация, появилась изначально в приложениях 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 и ничего не возвращающий.
Далее в нём пишем следующий код:

private void RegisterRoutes(RouteCollection routes)
        {
            Route routeItem = routes.MapPageRoute("Default", "/Default/", "~/Default.aspx");
        }

Итак, в приведенном выше коде, мы создаем и сразу добавляем маршрут в таблицу с помощью метода MapPageRoute, который в качестве параметров принимает следующее:

  • Название маршрута, значение должно быть уникальным для приложения
  • Желаемый маршрут
  • Страница-обработчик

Необходимо отметить, что желаемый маршрут не должен начинаться на / и на ~, а также не должен содержать знак ?.

Теперь необходимо вызвать наш метод, передав ему коллекцию маршрутов из таблицы

protected void Application_Start(object sender, EventArgs e)
        {
            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 paging", @"press/news/{page}/", "~/Default.aspx", false, new RouteValueDictionary { { "page", null }}, new RouteValueDictionary { {"page" , @"(page-d)?"} });

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 paging", @"press/news/{page}/", "~/Default.aspx", false, new RouteValueDictionary { { "page", null } }, new RouteValueDictionary { { "page", @"(page-d)?" } }, new RouteValueDictionary { {"hidden_param", 998} });

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 и напишем следущий код

var values = RouteData.Values;

Дело в том, что на странице нам доступно свойство RouteData страницы, которое содержит все необходимые данные. Значение определённого параметра можно получить по ключу в виде строки, например, RouteData.Values["alias"] на выходе получаем объект. Те данные, что скрыты, можно получить через RouteData.DataTokens["hidden_param"] на выходе также объект.

Ну, и на последок, генерация ссылок осуществляется через метод GetRouteUrl, которому на вход оптимальнее всего передавать первым параметром имя маршрута, которое определено в файле Global.asax, а вторым параметром словарь параметров для маршрута, например alias, id, page и т.д..

string url = GetRouteUrl("current news item", new System.Web.Routing.RouteValueDictionary { { "alias", "all" }, { "id", 23 } });

На выходе получаем относительную ссылку /press/news/all/23/ можно, в принципе, эту же ссылку написать вручную – разницы нет.

Примечание: при переходе со стандартной адресации на адресацию через маршруты страницы сайта будут доступны и по новым и по старым адресам, но вот получение переданных параметров будет различно. В качестве решения от себя могу предложить написать расширяющий метод для HttpRequest, который будет доступен в экземпляре Request и который будет единственным интерфейсом получения данных по ключу. Что-то наподобие object Request.GetData(string key).

Примечание: для редиректа можно использовать Response.RedirectToRoute() , где в качестве параметра указать имя маршрута и/или нужные параметры.

Примечание: ещё одна интересная вещь, допустим если у вас есть конкретные адреса вида default.aspx, list.aspx и т.д. и хотелось бы получить на выходе default/ и list/, то вполне возможно написать общий маршрут вида

routes.MapPageRoute("test", "{page}/", "~/{page}.aspx");

Если есть параметры, то вполне можно указать их в исходном адресе в {название параметра} и в конечном маршруте.

Подводя итог можно выделить следующие интересные моменты и преимущества:

  • Маршруты достаточно гибко настраиваются и могут быть перенесены в базу
  • Для каждого параметра можно указать допустимые значения (можно больше не опасаться нечто вроде калябаля в параметре, вместо этого будет сгенерирована 404, а не 500), т.е. избавляемся от лишних проверок в коде
  • Если учесть, что это встроенная вещь, то есть надежда, что она работает быстрее надстроек
  • Хорошо для SEO
  • Можно обойтись без надстроек
  • Это не редирект и не реврайтинг
  • Возможность однозначной выполнения сценария на странице (например, когда страница обработчик работает с различными вариантами ключей, можно просто передать какой-то скрытый параметр и по нему на странице отработать сценарий)

Ссылки на материалы:




Комментарии:

Оставить комментарий