Я пытаюсь найти хороший универсальный способ канонизации URL-адресов в приложении ASP.NET MVC 2. Вот что я придумал на данный момент:
// Using an authorization filter because it is executed earlier than other filters
public class CanonicalizeAttribute : AuthorizeAttribute
{
public bool ForceLowerCase { get;set; }
public CanonicalizeAttribute()
: base()
{
ForceLowerCase = true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
RouteValueDictionary values = ExtractRouteValues(filterContext);
string canonicalUrl = new UrlHelper(filterContext.RequestContext).RouteUrl(values);
if (ForceLowerCase)
canonicalUrl = canonicalUrl.ToLower();
if (filterContext.HttpContext.Request.Url.PathAndQuery != canonicalUrl)
filterContext.Result = new PermanentRedirectResult(canonicalUrl);
}
private static RouteValueDictionary ExtractRouteValues(AuthorizationContext filterContext)
{
var values = filterContext.RouteData.Values.Union(filterContext.RouteData.DataTokens).ToDictionary(x => x.Key, x => x.Value);
var queryString = filterContext.HttpContext.Request.QueryString;
foreach (string key in queryString.Keys)
{
if (!values.ContainsKey(key))
values.Add(key, queryString[key]);
}
return new RouteValueDictionary(values);
}
}
// Redirect result that uses permanent (301) redirect
public class PermanentRedirectResult : RedirectResult
{
public PermanentRedirectResult(string url) : base(url) { }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.RedirectPermanent(this.Url);
}
}
Теперь я могу размечать свои контроллеры следующим образом:
[Canonicalize]
public class HomeController : Controller { /* ... */ }
Кажется, все это работает достаточно хорошо, но у меня есть следующие проблемы:
Мне все еще нужно добавить CanonicalizeAttribute
для каждого контроллера (или метода действия), который я хочу канонизировать, когда сложно представить ситуацию, в которой я не хочу такого поведения. Похоже, что должен быть способ добиться такого поведения для всего сайта, а не для одного контроллера за раз.
Тот факт, что я реализую правило «принудительно переводить в нижний регистр» в фильтре, кажется неправильным. Конечно, было бы лучше каким-то образом включить это в логику URL-адреса маршрута, но я не могу придумать способ сделать это в моей конфигурации маршрутизации. Я подумал о добавлении @ "[az] *"
ограничений к параметрам контроллера и действия (а также к любым другим параметрам строкового маршрута), но я думаю, что это приведет к тому, что маршруты не будут сопоставлены. Кроме того, поскольку правило нижнего регистра не применяется на уровне маршрута, на моих страницах можно создавать ссылки с прописными буквами, что кажется довольно плохим.
Есть ли что-то очевидное? Я здесь вы не видите?