Маршрутизация ASP.NET

Visual Studio 2010

Обновлен: Ноябрь 2007

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

В приложении ASP.NET, не использующем маршрутизацию, входящий запрос URL-адреса обычно сопоставляется физическому файлу на диске, например ASPX-файлу. Например, запрос адреса http://server/application/Products.aspx?id=4 сопоставляется файлу Products.aspx, содержащему код и разметку для отображения результата запроса в веб-обозревателе. Веб-страница использует значение в строке запроса id=4, чтобы определить, какой тип содержания следует отображать, однако это значение вряд ли будет иметь смысл для пользователя.

При маршрутизации ASP.NET задаются шаблоны URL-адресов, содержащие местозаполнители для значений, используемых при обработке URL-запросов. Во время выполнения части URL-адреса, следующие за именем приложения, разбиваются на отдельные значения в зависимости от заданного шаблона URL-адреса. Например, при запросе адреса http://server/application/Products/show/beverages маршрутный анализатор может передавать значения Products, show и beverages в обработчик для запроса. В случае же, если запрос не контролируется механизмом маршрутизации URL-адресов, фрагмент /Products/show/beverages интерпретируется как путь к файлу в приложении.

Можно также использовать шаблоны URL-адресов для программного создания URL-адресов, соответствующих маршрутам. Это позволяет централизовать логику создания гиперссылок в приложении ASP.NET.

Маршрутизация ASP.NET отличается от других схем перезаписи URL-адресов. При перезаписи URL-адресов входящие запросы обрабатываются путем фактического изменения URL-адреса до отправки запроса веб-странице. Например, приложение, использующее перезапись URL-адресов, может заменить URL-адрес /Products/Widgets/ на /Products.aspx?id=4. Кроме того, при перезаписи URL-адресов обычно отсутствует API для создания URL-адресов на основе шаблонов. Если при перезаписи URL-адресов изменяется шаблон URL-адреса, необходимо вручную обновлять все гиперссылки, содержащие первоначальный URL-адрес.

При маршрутизации ASP.NET URL-адрес не изменяется при обработке входящего запроса, поскольку механизм маршрутизации позволяет извлекать значения из URL-адреса. При необходимости создать URL-адрес значения параметров передаются в метод, автоматически генерирующий URL-адрес. Чтобы изменить шаблон URL-адреса, достаточно изменить его в одном месте, и все создаваемые в приложении ссылки, которые основаны на этом шаблоне, будут автоматически использовать новый шаблон.

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

В маршруте местозаполнители (называемые также параметрами URL-адреса) определяются путем их заключения в фигурные скобки ( { и } ). Символ / при анализе URL-адреса воспринимается как разделитель. Информация в определении маршрута, отличная от разделителей и не заключенная в фигурные скобки, воспринимается как постоянное значение. Значения, извлекаемые между разделителями, присваиваются местозаполнителям.

Между разделителями можно определять несколько местозаполнителей, но они должны быть разделены постоянным значением. Например, {language}-{country}/{action} является допустимым шаблоном маршрута. Однако {language}{country}/{action} не является допустимым шаблоном, поскольку между местозаполнителями нет постоянного значения или разделителя. Поэтому механизм маршрутизации не может определить, каким образом следует отделять значение местозаполнителя language от значения местозаполнителя country.

В следующей таблице приведены допустимые шаблоны маршрутов и примеры URL-запросов, соответствующих этим шаблонам.

Определение маршрута

Пример соответствующего URL-адреса

{controller}/{action}/{id}

/Products/show/beverages

{table}/Details.aspx

/Products/Details.aspx

blog/{action}/{entry}

/blog/show/123

{reporttype}/{year}/{month}/{day}

/sales/2008/1/5

{locale}/{action}

/en-US/show

{language}-{country}/{action}

/en-US/show

Как правило, добавление маршрутов выполняется в методе, который вызывается из обработчика события Application_Start в файле Global.asax. Такой подход обеспечивает доступность маршрутов при запуске приложения. Он также обеспечивает возможность непосредственного вызова метода при модульном тестировании приложения. Если при модульном тестировании приложения требуется выполнять непосредственный вызов метода, тогда метод, регистрирующий маршруты, должен быть статическим (Shared в Visual Basic) и должен иметь параметр RouteCollection.

Добавление маршрутов выполняется путем их добавления в статическое свойство Routes класса RouteTable. Свойство Routes представляет собой объект RouteCollection, содержащий все маршруты приложения ASP.NET. В следующем примере приведен код из файла Global.asax, который добавляет объект Route, определяющий два параметра URL: action и categoryName.

protected void Application_Start(object sender, EventArgs e)
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
         "Category/{action}/{categoryName}"
         , new CategoryRouteHandler()
    ));
}

При определении маршрута можно назначать для параметров значения по умолчанию. Значение по умолчанию используется, если значение параметра не указано в URL-адресе. Значения по умолчанию для маршрута устанавливаются путем присвоения словаря свойству Defaults класса Route. В следующем примере приведен маршрут со значениями по умолчанию.

void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary 
           {{"categoryName", "food"}, {"action", "show"}}
     }
  );
}

При обработке URL-запроса механизмом маршрутизации ASP.NET определение маршрута, приведенное в примере (с заданными по умолчанию значениями food для categoryName и show для action), возвращает результаты, представленные в следующей таблице.

URL-адрес

Значения параметров

/Category

action = "show" (значение по умолчанию)

categoryName = "food" (значение по умолчанию)

/Category/add

action = "add"

categoryName = "food" (значение по умолчанию)

/Category/add/beverages

action = "add"

categoryName= "beverages"

Иногда необходимо обрабатывать URL-запросы, содержащие переменное количество сегментов. При определении маршрута можно указать, что последний параметр должен соответствовать остальной части URL-адреса, отметив его звездочкой (*). Такой параметр называется универсальным параметром. Маршрут с универсальным параметром будет также соответствовать URL-адресам, не содержащим значения для последнего параметра. В следующем примере представлен шаблон маршрута, соответствующий неизвестному количеству сегментов.

query/{queryname}/{*queryvalues}

При обработке URL-запроса механизмом маршрутизации ASP.NET определение маршрута, приведенное в примере, возвращает результаты, представленные в следующей таблице.

URL-адрес

Значения параметров

/query/select/bikes/onsale

queryname = "select"

queryvalues = "bikes/onsale"

/query/select/bikes

queryname = "select"

queryvalues = "bikes"

/query/select

queryname = "select"

queryvalues = пустая строка

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

Ограничения определяются с использованием регулярных выражений или с использованием объектов, реализующих интерфейс IRouteConstraint. При добавлении определения маршрута в коллекцию Routes ограничения добавляются путем создания объекта RouteValueDictionary, содержащего тест проверки. Затем этот объект назначается в качестве значения свойства Constraints. Ключ в словаре идентифицирует параметр, к которому применяется ограничение. В качестве значения в словаре можно использовать либо строку, представляющую регулярное выражение, либо объект, реализующий интерфейс IRouteConstraint.

При использовании строки механизм маршрутизации воспринимает ее как регулярное выражение и проверяет допустимость значения параметра, вызывая метод IsMatch класса Regex. Регулярное выражение всегда рассматривается без учета регистра. Дополнительные сведения см. в разделе Регулярные выражения в .NET Framework.

При использовании объекта IRouteConstraint механизм маршрутизации ASP.NET проверяет допустимость значения параметра, вызывая метод Match объекта IRouteConstraint. Метод Match возвращает логическое значение, указывающее на допустимость значения.

В следующем примере приведены ограничения, задающие диапазон значений параметров locale и year.

void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
    routes.Add(new Route
    (
      "{locale}/{year}"
         , new ReportRouteHandler()
    )
       {
          Constraints = new RouteValueDictionary 
          {{"locale", "[a-z]{2}-[a-z]{2}"},{year, @"\d{4}"}}
       });
}

При обработке URL-запроса механизмом маршрутизации определение маршрута, приведенное в предыдущем примере, возвращает результаты, представленные в следующей таблице.

URL-адрес

Результат

/en-US

Нет совпадения. Оба параметра (locale и year) являются обязательными.

/en-US/08

Нет совпадения. Ограничение параметра year требует использовать 4 цифры.

/en-US/2008

locale = "en-US"

year = "2008"

По умолчанию механизм маршрутизации не обрабатывает запросы, сопоставляемые существующему физическому файлу на веб-сервере. Например, запрос http://server/application/Products/Beverages/Coffee.aspx не обрабатывается механизмом маршрутизации, если по адресу Products/Beverages/Coffee.aspx существует физический файл. Механизм маршрутизации не обрабатывает запрос, даже если он соответствует определенному шаблону, например {controller}/{action}/{id}.

Если необходимо, чтобы механизм маршрутизации обрабатывал все запросы, включая запросы, которые указывают на файлы, можно переопределить заданную по умолчанию функциональность, задав для свойства RouteExistingFiles объекта RouteCollection значение true. При присвоении значения true все запросы, соответствующие заданному шаблону, обрабатываются механизмом маршрутизации.

Можно также настроить механизм маршрутизации таким образом, чтобы он не обрабатывал некоторые URL-запросы. Для того чтобы механизм маршрутизации не обрабатывал определенные запросы, нужно определить маршрут и указать, что для обработки этого шаблона следует использовать класс StopRoutingHandler. При обработке запроса объектом StopRoutingHandler объект StopRoutingHandler блокирует дополнительную обработку запроса в качестве маршрута. Вместо этого запрос обрабатывается как страница ASP.NET, веб-служба или другая конечная точка ASP.NET. Например, чтобы не допустить обработки механизмом маршрутизации запросов файла WebResource.axd, можно добавить следующее определение маршрута.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRouteHandler()));
}

При обработке URL-запросов механизмом маршрутизации выполняется попытка сопоставить URL-адрес запроса маршруту. Сопоставление URL-запроса маршруту зависит от выполнения следующих условий.

  • Определенные пользователем шаблоны маршрутов или шаблоны маршрутов по умолчанию (если таковые имеются), включенные в тип проекта.

  • Порядок их добавления в коллекцию Routes.

  • Любые значения по умолчанию, заданные для маршрута.

  • Любые ограничения, заданные для маршрута.

  • Определена ли в механизме маршрутизации обработка запросов, соответствующих физическому файлу.

Чтобы избежать обработки запроса неверным обработчиком, необходимо учитывать все эти условия при определении маршрутов. Порядок следования объектов Route в коллекции Routes имеет большое значение. Сопоставление маршрутов выполняется, начиная с первого и заканчивая последним маршрутом в коллекции. При обнаружении соответствия оценка маршрутов прекращается. В целом, нужно добавлять маршруты в свойство Routes, начиная с наиболее конкретных определений маршрутов и завершая наименее конкретными.

Например, предположим, что были добавлены маршруты со следующими шаблонами.

  • Маршрут 1: {controller}/{action}/{id}

  • Маршрут 2: products/show/{id}

Маршрут 2 никогда не будет обрабатывать запрос, так как сначала оценивается маршрут 1, который всегда соответствует запросам, описанным маршрутом 2. Запрос http://server/application/products/show/bikes в большей степени соответствует маршруту 2, однако он обрабатывается маршрутом 1 с использованием следующих значений.

  • controller = products

  • action = show

  • id = bikes

Если параметр отсутствует в запросе, для него используется значение по умолчанию. В этом случае маршрут может быть сопоставлен неожиданному запросу. Например, предположим, что были добавлены маршруты со следующими шаблонами.

  • Маршрут 1: {report}/{year}/{month} со значениями по умолчанию для параметров year и month.

  • Маршрут 2: {report}/{year} со значением по умолчанию для параметра year.

Маршрут 2 никогда не будет обрабатывать запрос. Маршрут 1 может быть предназначен для ежемесячного отчета, а маршрут 2 — для годового отчета. Однако значения, установленные по умолчанию для маршрута 1, приводят к тому, что он будет соответствовать любому запросу, который также описывается маршрутом 2.

Чтобы избежать неоднозначности в шаблонах, можно включить постоянные значения, такие как annual/{report}/{year} и monthly/{report}/{year}/{month}.

Если URL-адрес не соответствует ни одному объекту Route, определенному в коллекции RouteTable, механизм маршрутизации ASP.NET не обрабатывает этот запрос. Вместо этого обработка передается странице ASP.NET, веб-службе или другой конечной точке ASP.NET.

Если необходимо централизовать логику построения URL-адресов, то для формирования URL-адресов можно использовать маршруты. URL-адрес создается путем передачи значений параметров в виде словаря в метод GetVirtualPath объекта RouteCollection. Метод GetVirtualPath ищет первый маршрут в объекте RouteCollection, соответствующий параметрам в словаре. Соответствующий маршрут используется для формирования URL-адреса. Определение маршрута приведено в следующем примере.

public static void RegisterRoutes(RouteCollection routes)
{
  routes.Add(new Route
  (
     "Category/{action}/{categoryName}"
          new CategoryRouteHandler()
  )
    {
       Defaults = new RouteValueDictionary {{"categoryName", "food"}, 
           {"action", "show"}}
     }
  );
}

В следующем примере приведен код элемента управления, создающего URL-адрес на основе маршрута.

Dim urlParameters As RouteValueDictionary

urlParameters = New RouteValueDictionary(New With {.categoryName = "beverages", _
        .action = "summarize"})
HyperLink1.NavigateUrl = RouteTable.Routes.GetVirtualPath _
    (context, urlParameters).VirtualPath

HyperLink1.NavigateUrl = RouteTable.Routes.GetVirtualPath
  (context,
  new RouteValueDictionary { 
    { "categoryName", "beverages" }, 
    {"action", "summarize" }}
  ).VirtualPath;

В результате выполнения этого кода элемент управления HyperLink1 будет содержать значение "Category/summarize/beverages" в свойстве NavigateUrl.

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

Показ: