如果你有一些需要重定向網頁 URL 的情況,可以返回 HTTP 狀態碼 301/302 告訴瀏覽器或者搜索引擎訪問新的 URL。本文描述如何在 ASP.NET Core 中進行重定向。

HTTP 狀態碼 301/302

301 表示“Moved Permanently”,即永久移動。通過返回此狀態碼可以告知瀏覽器或者搜索引擎此 URL 已經永久移動到了新的 URL 地址。搜索引擎會使用新的 URL 來更新自己的搜索結果,而瀏覽器會將此 URL 重定向緩存起來,下次訪問的時候直接使用新的 URL 來訪問。

302 表示“Found”,發現;原始描述爲“Moved Temporarily”,即臨時移動。通過返回此狀態碼可以告知瀏覽器或者搜索引擎此 URL 臨時移動到了新的 URL 地址。搜索引擎會使用此新的 URL 來抓取頁面的內容但不會更新此 URL,而瀏覽器會訪問新的 URL 但不會緩存此 URL 重定向。

還有其他的重定向的 HTTP 狀態碼:

  • 303 See Other
  • 307 Temporary Redirect
  • 308 Permanent Redirect

301/302 本來設計爲移動資源的時候保持方法不變,但各大瀏覽器在實現的時候對於 POST 方法,有的實現成了 GET 方法,有的實現成了 POST 方法。於是在後來的 HTTP 標準中將瀏覽器的錯誤實現變成了標準,301 和 302 方法要求使用 GET 方法重定向。不過由於歷史原因無法保證一定是改用 GET 方法,所以增加了 303 狀態碼要求一定使用 GET 方法重定向。隨後將原來本應該正確實現的 301 和 302 重新定義成 307 和 308 狀態碼,要求重定向時不允許修改方法。

ASP.NET Core

ASP.NET Core 的 Blazor 框架生成的頁面在路由的時候是不識別 .html 後綴的,而帶有 .html 後綴的 URL 會被識別爲靜態文件。於是,如果創建了一個空的 Blazor 應用,當訪問 https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html 網址的時候,會返回 404 Not Found,而不是路由到我的博客頁面。

如果我們將此 URL 重定向到不帶後綴的 URL,則可以被 Blazor 框架識別並正確顯示對應的博客頁面。

我們有兩個不同的方式來實現這種 URL 的重定向:

  1. 做一個重定向的控制器 Controller ,然後在控制器中重定向所有的博客頁面
  2. 做一個重定向的中間件,對所有包含 .html 後綴的博客頁面重定向到沒有 .html 後綴的博客頁面

不過,寫一個 Controller 會要求這個 Controller 路由到幾乎所有的 URL 上,對其他功能很不利,所以中間件是最合適的方式。

重定向中間件

public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
++          app.UseAutoRemoveHtmlExtension();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
            app.UseStaticFiles();
        }
    }

Startup 類的 Configure 方法中可以添加中間件。爲了實現去掉 .html 後綴的中間件,我添加了一個自己的擴展方法 UseAutoRemoveHtmlExtension

/// <summary>
/// 自動移除所有的 .html 後綴,並永久重定向到沒有 .html 後綴的網頁。
/// </summary>
/// <param name="app"><see cref="IApplicationBuilder"/>。</param>
/// <returns><see cref="IApplicationBuilder"/>。</returns>
public static IApplicationBuilder UseAutoRemoveHtmlExtension(this IApplicationBuilder app) => app.Use(async (context, next) =>
{
    var urlPath = context.Request.Path.HasValue
        ? context.Request.Path.Value
        : "";
    if (urlPath.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
    {
        // 去掉 .html 後綴
        var url = urlPath[0..^5];
        context.Response.Redirect(url);
        context.Response.StatusCode = 301;
        return;
    }
    await next().ConfigureAwait(false);
});

實現自己的中間件實際上直接調用 IApplicationBuilder 中的 Use 方法即可,傳入一個委託用來在 URL 處理過程中添加一個步驟。

兩個參數, context 中包含了本次請求的一些上下文,包括域名、URL 路徑,返回的 HTTP 狀態碼。調用 context.Response.Redirect 方法可以進行 302 跳轉。如果需要改成 301 跳轉,則直接設置 context.Response.StatusCode 方法即可。

接下來,對於不需要重定向的網址,我們直接交給後面的中間件處理,調用 await next()

重定向

如果你希望做其他種類的跳轉,你也可以添加新的中間件,比如:

  1. 將 HTTP 重定向到 HTTPS(谷歌建議使用 301 跳轉)
  2. 你可以在打開某個網頁之前要求登錄,於是做一個 302 跳轉到登錄頁面;
  3. 你可以將一些已經過時的網頁進行 301 跳轉到新的網頁;
    • 比如我將一些之前不太規範的博客 URL 重定向到統一的格式;
  4. 你可以在遷移服務的時候臨時做一個 302 跳轉。

小心緩存

請注意,301 重定向會被瀏覽器緩存。也就是說如果你重定向到了一個錯誤的網址,那麼再次訪問的話瀏覽器將直接訪問這個錯誤的網址。如果希望瀏覽器停止重定向到這個錯誤的網址,需要清除瀏覽器的緩存。所以使用 301 的時候需要謹慎一些。

參考資料

相關文章