将特殊字符重定向到它们的 "escaped URL form"

Redirect special characters to their "escaped URL form"

我网站上的 URL (ASP.NET MVC 4.6) 如下所示: /test/sog

但是,我的网站是丹麦语,所以我想在所有 URL 中将丹麦语特殊字符“ø”重定向到 'o'。

在上述情况下,对 /test/søg 的请求应该重定向到 /test/sog.

我的网站相当大,所以我想在整个网站范围内执行此操作(而不必为每个 URL 创建单独的重定向控制器方法)。

我该怎么做?

这是一个有趣的问题,我想知道答案。事实证明这是相当简单的。要实现这一点,您确实需要两件事。

  1. 将字符串转换为罗马字符。
  2. 执行 301 重定向到新 URL。

将字符串转换为罗马字符

我找到了答案here,但将其修改为扩展方法以使其更易于使用。

using System;
using System.Globalization;
using System.Text;

public static class StringExtensions
{
    public static string RemoveDiacritics(this string text)
    {
        var normalizedString = text.Normalize(NormalizationForm.FormD);
        var stringBuilder = new StringBuilder();

        foreach (var c in normalizedString)
        {
            var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
            if (unicodeCategory != UnicodeCategory.NonSpacingMark)
            {
                stringBuilder.Append(c);
            }
        }

        return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
    }
}

执行 301 重定向到新 URL

这有点复杂。虽然您 可以 进行 302 重定向(所有浏览器都遵守),但 301 对 SEO 更友好,所以这就是我在这里展示的方法。遗憾的是,某些浏览器不会自动遵循 301 重定向。因此,需要一些额外的步骤来确保如果浏览器不这样做,用户将通过客户端 302 重定向进行重定向,如果失败,则显示 link ,他们可以在其中跳转到下一页.

RedirectToRomanizedUrlPermanent.cs

我们首先使用一条路线来清洁 URL。如果干净的 URL 与原来的不同(即替换了一个或多个字符),我们将请求发送到名为 SystemController 的控制器来处理 301 重定向。

public class RedirectToRomanizedUrlPermanent : RouteBase
{
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var path = httpContext.Request.Path;
        var romanPath = path.RemoveDiacritics();
        if (!path.Equals(romanPath, StringComparison.OrdinalIgnoreCase))
        {
            var routeData = new RouteData(this, new MvcRouteHandler());

            routeData.Values["controller"] = "System";
            routeData.Values["action"] = "Status301";
            routeData.DataTokens["redirectLocation"] = romanPath;

            return routeData;
        }

        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        return null;
    }
}

SystemController.cs

接下来,我们有我们的控制器,其目的是实际提供 301 状态。如果浏览器不遵守 301,则会向用户显示一个视图。

using System.Net;
using System.Web.Mvc;

public class SystemController : Controller
{
    public ActionResult Status301()
    {
        var redirectLocation = this.Request.RequestContext.RouteData.DataTokens["redirectLocation"] as string;

        // IMPORTANT: Don't cache the 301 redirect because browsers tend to keep it around
        // meaning you cannot update the browser to a different URL from the server side.
        Response.CacheControl = "no-cache";
        Response.StatusCode = (int)HttpStatusCode.MovedPermanently;
        Response.RedirectLocation = redirectLocation;

        ViewBag.RedirectLocation = redirectLocation;
        return View();
    }
}

/Views/System/Status301.cshtml

视图将尝试通过 Meta-Refresh JavaScript 自动重定向用户。这两个都可以在浏览器中关闭,但用户很可能会把它带到他们应该去的地方。如果没有,您应该告诉用户:

  1. 该页面有一个新位置。
  2. 如果没有自动重定向,他们需要单击 link。
  3. 他们应该将书签更新到新的 URL。

@{
    ViewBag.Title = "Page Moved";
}
@section MetaRefresh {
    <meta http-equiv="refresh" content="5;@ViewBag.RedirectLocation" />
}

<h2 class="error">Page Moved</h2>
<p>
    The page has a new location. Click on the following link if you are 
    not redirected automatically in 5 seconds. Please update your bookmark(s) to the new location.
</p>
<a href="@ViewBag.RedirectLocation">@ViewBag.RedirectLocation</a>.

<script>
    //<!--
    setTimeout(function () {
        window.location = "@ViewBag.RedirectLocation";
    }, 5000);
    //-->
</script>

用法

向应用程序的 _Layout.cshtml 页面添加一个部分,以便元刷新可以放置在页面的 <head> 部分。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - My ASP.NET MVC Application</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <!-- Add this so the view can update this section -->
        @RenderSection("MetaRefresh", required: false)
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>

    <!-- layout code omitted -->

</html>

用 MVC 注册路由。重要的是,这发生在任何其他路由被注册之前(包括对 MapMvcAttributeRoutesAreaRegistration.RegisterAllAreas 的任何调用)。

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // IMPORTANT: Register this before any other routes.
        routes.Add(new RedirectToRomanizedUrlPermanent());

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

现在当您输入 URL 例如 /Ĥőmȩ/Ċőńţãçť 时,您会自动重定向到 /Home/Contact