SEF-адреса в ASP.NET 2.0 WebForms на IIS6: полное решение (работающий postback, ASP.NET AJAX)
Задача реализации «красивых» URL (вида /catalog/notebooks/asus/) в ASP.NET 2.0 приложении, работающем на IIS6 требует
нескольких дополнительных действий.
Настройка IIS
В случае, если нужно использовать т.н. extensionless URLs, т.е. адреса вида /catalog/notebooks/asus/, а не, например, /catalog/notebooks/asus.aspx)
необходимое условие — настройка wildcard mapping'a в IIS.
Считается, что это несколько снижает производительность, т.к. теперь все запросы, включая запросы к статическим файлам, будут проходить через ASP.NET ISAPI-расширение, однако, другого способа на IIS6 нет.
Аналог mod_rewrite
Существует много реализаций самого механизма URL-реврайтинга, например, UrlRewritingNet. Компонент устанавливается
как ASP.NET HTTP-модуль и конфигурируется через свою секцию в Web.config.
<urlrewritingnet rewriteOnlyVirtualUrls="true" contextItemsPrefix="QueryString" xmlns="http://www.urlrewriting.net/schemas/config/2006/07"> <rewrites> <!-- Do not delete! --> <add name="RootOfTheSite" virtualUrl="^~/$" rewriteUrlParameter="ExcludeFromClientQueryString" destinationUrl="~/Default.aspx" ignoreCase="true"/> <!-- /Do not delete! --> <add name="TextPages" virtualUrl="^~/pages/(.+?)/?$" rewriteUrlParameter="ExcludeFromClientQueryString" destinationUrl="~/TextPagesPage.aspx?shortcut=$1" ignoreCase="true"/> ... </rewrites> </urlrewritingnet>
Сама процедура реврайтинга проблем не представляет, а вот часть отмеченная комментариями необходима, без неё при заходе в корень сайта IIS будет отдавать ошибку 404.
Postback
Следующей проблемой будет Postback, для решение этой проблемы можно использовать простой control adapter, который просто уберёт из тега <form>...</form>,
оборачивающего всю страницу атрибут action="". Без указания этого атрибута postback будет идти на текущий адрес.
Решение состоит из двух компонентов, файла FormAction.browser (помещается в папку App_Browsers), с таким содержанием:
<browsers> <browser refID="Default"> <controlAdapters> <adapter controlType="System.Web.UI.HtmlControls.HtmlForm" adapterType="CrispStudio.Web.ControlAdapters.FormAction" /> </controlAdapters> </browser> </browsers>
И самого класса адаптера:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.Adapters;
using System.IO;
namespace CrispStudio.Web.ControlAdapters
{
public class FormAction : ControlAdapter
{
protected override void Render(HtmlTextWriter writer)
{
base.Render(new FormActionWriter(writer));
}
}
public class FormActionWriter : HtmlTextWriter
{
public FormActionWriter(HtmlTextWriter writer)
: base(writer)
{
this.InnerWriter = writer.InnerWriter;
}
public FormActionWriter(TextWriter writer)
: base(writer)
{
this.InnerWriter = writer;
}
public override void WriteAttribute(string name, string value, bool fEncode)
{
if (name != "action")
{
base.WriteAttribute(name, value, fEncode);
}
}
}
}
Автор решения — Scott Guthrie
Адреса на сайте
Для того, чтобы везде в ссылках можно было использовать относительные адреса удобно использовать тег <base /> в <head>.
С этим тесно связана ещё одна проблема. Большинство ASP.NET-сайтов используют <head runat="server"> чтобы программно изменять <title>, в этом случае ASP.NET
пытается «исправить» адреса CSS и JavaScript-скриптов в <head>, если они не абсолютные. Для решения проблемы
нужно просто использовать компонент PlaceHolder:
<head runat="server">
<title></title>
<asp:PlaceHolder id="cphIncludes" runat="server">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<base href="<%=Utils.SiteUrl%>" />
<link rel="stylesheet" type="text/css" href="css/styles.css" media="all"/>
<script type="text/javascript" src="js/my_script.js" encoding="utf-8"></script>
<link rel="shortcut icon" type="image/x-icon" href="images/favicon.ico" />
</asp:PlaceHolder>
</head>
....
<img src="images/logo.png />
Генерация исходящих URL (потерянный фрагмент головоломки)
Один из вариантов генерации исходящих URL'ов — хранение форматов адресов в виде строк в ресурсах и подстановка нужных
значений через String.Format(). Есть и более красивые варианты.
