Главная > ASP.NET > ЧПУ (красивый урл) в ASP.NET. Routing Webforms.

ЧПУ (красивый урл) в ASP.NET. Routing Webforms.

ЧПУ — человекопонятный урл. ЧПУ подразумевает под собой такие url-адреса, которые легко запомнить и которые показывают логическую структуру данных на сервере. Т.е. урлы вида:

mysite.com/?id=photos&time=summer-2009
mysite.com/?id=archives&year=2009
mysite.com/?id=shop&action=orders

не используют ЧПУ, а следующие адреса:

mysite.com/photos/summer-2009/
mysite.com/archives/2009/
mysite.com/shop/orders/

используют и дают понять, что по первой ссылке у нас на сайте находятся летние фотографии 2009 года, по второй — архивы материалов за 2009 год, ну а по третьей — ваши заказы, оформленные в онлайн-магазине. Кроме того, логично предположить, что если пользователь уберет из урла последнюю часть, например «summer-2009», то он попадет на один уровень выше, например, на все архивы фотографий. Это очень удобно, прежде всего не из-за того, что такие адреса можно запомнить (врядли кто будет запоминать длинный урл, будь он даже с ЧПУ), а именно с точки зрения разделения логической структуры данных.

На сервере Apache для ЧПУ используется так называемый mod_rewrite. На сервере IIS имеются свои приблуды, о которых я сейчас расскажу, хочется сразу сказать, что если на IIS все было бы так же просто как на Apache, то этого поста в блоге не было бы.

Какие способы переписывания урлов использовались в ASP.NET? Во-первых непосредственно возможности ASP.NET 2.0, а именно urlMappings. Во-вторых, применяя стороннюю библиотеку, например UrlRewritingNet.UrlRewrite, хотя вы можете использовать любую другую. Далее, широко применялись ISAPI-фильтры — ISAPI Rewrite. И наконец, с выходом IIS7 многие стали пользоваться стандартным модулем Url Rewrite. Теперь о недостатках подходов:

  • urlMappings — очень ограниченные возможности;
  • сторонние библиотеки — неудобство подключения и использования как на локальном, так и на веб-сервере, некоторые другие проблемы, с которыми приходится сталкиваться уже во время работы;
  • ISAPI Rewrite — работает только на IIS, а следовательно, если вы разрабатываете веб-приложение на локальном сервере, вы не сможете воспользоваться этим фильтром;
  • Mod Rewrite — имеется только на IIS 7, характерны все те же неудобства, как и в случае с ISAPI Rewrite;

Более подробно с этими вещами можете ознакомиться на хабре http://habrahabr.ru/blogs/net/24666/. Хочу добавить, что ни один из этих способов мне не подходит. Почему? Ответ прост — я хочу разрабатывать приложение так, чтобы при разворачивании его на сервере, мне бы не приходилось прописывать многочисленные правила переписывания урлов, подключать какие-то сторонние библиотеки и тд. Покупая, к примеру, автомобиль, вы ведь не хотите тратить время на его сборку? Это обычное желание, чтобы все работало «из коробки».

Возможно, кто-то не знает, но существует еще один способ получения «красивых» урл-адресов, называется он «Routing». «Routing» широко применяется в ASP.NET MVC, в классических Webforms про него почему-то забыли или не слышали, во всяком случае, большинство продолжают рекомендовать использование ISAPI Rewrite или Mod Rewrite. На мой взгляд, «Routing» не должен восприниматься как возможность получения человекопонятных урлов, прежде всего он помогает разделить логику, ну а красивые адреса — это в придачу 🙂 .

Все необходимое для работы Routing`a уже есть в .NET Framework 3.5, а это значит, что для его работы вам потребуется добавить лишь несколько строк в web.config, чтобы начать им пользоваться. Как следствие, вы сможете работать с красивыми адресами как на локальном дев-сервере, так и на ИИС, без какой либо разницы! О какой разнице может идти речь? Чтобы понять, попробуйте использовать на ИИС7 модуль Url Rewrite, может возникнуть проблема относительных адресов всяческих скриптов, файлов css, файлов изображений и тд, да что говорить, даже ссылки могут генерироваться неверно. Довольно болтовни, переходим к делу.

web.config

<add assembly="System.Web.Routing, Version=3.5.0.0,
  Culture=neutral,
  PublicKeyToken=31BF3856AD364E35"/>

если IIS6 или IIS7 в Classic mode, то добавляем

<httpModules>
  <add name="UrlRoutingModule"
       type="System.Web.Routing.UrlRoutingModule,
             System.Web.Routing,
             Version=3.5.0.0,
             Culture=neutral,
             PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

если IIS7 в Integrated mode, то

<system.webServer>
  <modules>
    <remove name="UrlRoutingModule" />
    <add name="UrlRoutingModule"
         type="System.Web.Routing.UrlRoutingModule,
               System.Web.Routing,
               Version=3.5.0.0,
               Culture=neutral,
               PublicKeyToken=31BF3856AD364E35"/>
  </modules>
</system.webServer>

<system.webServer>
  <handlers>
    <add name="UrlRoutingHandler"
         preCondition="integratedMode"
         verb="*"
         path="UrlRouting.axd"
         type="System.Web.HttpForbiddenHandler,
               System.Web, Version=2.0.0.0,
               Culture=neutral,
               PublicKeyToken=b03f5f7f11d50a3a" />
  </handlers>
</system.webServer>

Далее, нам нужно сделать HTTP-обработчик, примерно такой, как CustomRouteHandler.cs:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Compilation;
using System.ServiceModel.Channels;
using System.Web.UI;
using System.Web.Routing;
using System.Web.Security;
using System.Net;

/// <summary>
/// Summary description for CustomRouteHandler
/// </summary>
public class CustomRouteHandler : IRouteHandler
{
    public CustomRouteHandler(string virtualPath)
    {
        this.VirtualPath = virtualPath;
    }

    public string VirtualPath { get; private set; }

    public IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
    {
        if (!UrlAuthorizationModule.CheckUrlAccessForPrincipal(VirtualPath, requestContext.HttpContext.User, requestContext.HttpContext.Request.HttpMethod))
        {
            requestContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            requestContext.HttpContext.Response.End();
        }

        var page = BuildManager.CreateInstanceFromVirtualPath(VirtualPath, typeof(Page)) as IHttpHandler;
        foreach (var urlParm in requestContext.RouteData.Values)
        {
            requestContext.HttpContext.Items[urlParm.Key] = urlParm.Value;
        }
        return page;
    }
}

Также, необходимо определить роуты в файле Global.asax:

    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        RegisterRoutes(RouteTable.Routes);
    }

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRoutingHandler()));
        routes.Add("Content1", new Route("{page}.html", new CustomRouteHandler("~/Default.aspx")));
    }

Из этих роутов видно, что все, что будет находится после http://mysite.com/ будет считать параметром «page» и будет перенаправляться на Default.aspx. Это как бы частный случай. Мы можем разделить логику, например, так:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.Add(new Route("{resource}.axd/{*pathInfo}", new StopRoutingHandler()));
        routes.Add("Content1", new Route("pages/{page}.html", new CustomRouteHandler("~/Default.aspx")));
        routes.Add("Photos", new Route("photos/{id}.html", new CustomRouteHandler("~/Photos.aspx")));
    }

Считывать параметры на страницах можно так:

Context.Items["page"];
Context.Items["id"];

В CustomRouteHandler.cs уже учтено, как будет вести себя приложение в случае запроса адреса, которому требуется аутентификация.

Вот несколько ссылок, по которым вы сможете ознакомиться с Routing`ом подробнее:
http://chriscavanagh.wordpress.com/2009/05/19/asp-net-webform-routing-with-sitemaps/
http://chriscavanagh.wordpress.com/2008/03/11/aspnet-routing-goodbye-url-rewriting/
http://www.gotdotnet.ru/Forums/Web/662504.aspx
http://www.gotdotnet.ru/Forums/Web/661416.aspx
http://www.develop.com/aspnet_azure
http://chriscavanagh.wordpress.com/2008/10/18/aspnet-routing-revisited-again/
http://chriscavanagh.wordpress.com/2008/04/25/systemwebrouting-with-webforms-sample/
http://chriscavanagh.wordpress.com/2008/11/06/aspnet-routing-just-enough-rope/
http://andir-notes.blogspot.com/2009/02/url-rewriting-systemwebrouting.html
http://msdn.microsoft.com/ru-ru/magazine/dd347546.aspx
http://msdn.microsoft.com/en-us/library/cc668202.aspx

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

Categories: ASP.NET Tags: ,
  1. akuba
    4 февраля 2010 в 13:09 | #1

    Спасибо, буквально спас — уже весь мозг высушил

  2. Михаил
    21 марта 2010 в 13:01 | #2

    Пример бы, а то постонно VS на все упомнинания routes в Global.asax. Может что в Global.asax прописать надо?

  3. admin
    21 марта 2010 в 19:52 | #3

    Так написано же, чего, где и как написать и что подключить.

  4. Михаил
    22 марта 2010 в 18:37 | #4

    @admin
    Все сделал как написано, сайт запускает ошибок нету, а вот работать механизм не хочет, при попытке обратиться pages/test.html вылазить ошибка что страница не найдена

  5. 22 марта 2010 в 23:10 | #5

    @Михаил
    Проверяйте внимательнее, код тестирован неоднократно. Или вышлите на мыло, посмотрю.

  6. Михаил
    23 марта 2010 в 17:56 | #6

    admin :@Михаил Проверяйте внимательнее, код тестирован неоднократно. Или вышлите на мыло, посмотрю.

    В общем не стал я маяться и взял пример с msdn http://msdn.microsoft.com/ru-ru/library/cc668202.aspx

  7. 23 марта 2010 в 21:16 | #7

    Ну эта статья прочитывалась перед написанием этой темы :), код наверное даже одинаковый в некоторых местах.

  8. Михаил
    24 марта 2010 в 19:10 | #8

    Хм, нашел свой касяк когда твоим методом пользовался то писал весь код не в Global.asax а в Global.asax.cs и похоже все процедуры были локальны внутри него. В общем твой код у меня то же сейчас заработал, при использовании мною Global.asax. Теперь вопрос в другом есть способ какой — нибудь приручить SiteMapPath к Routing?

  9. 25 марта 2010 в 18:41 | #9

    Вот в этой статье писал, только там про MVC, но это мало что меняет
    http://blog.webferia.ru/web/asp-net/asp-net-mvc-dlya-nachinayushhix-chast-3-treeview-bystryj-urok/ .

  1. Пока что нет уведомлений.