爲 ASP.NET Core 程序製作 URL 的 301/302 跳轉
如果你有一些需要重定向網頁 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 的重定向:
- 做一個重定向的控制器
Controller
,然後在控制器中重定向所有的博客頁面 - 做一個重定向的中間件,對所有包含
.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()
。
重定向
如果你希望做其他種類的跳轉,你也可以添加新的中間件,比如:
- 將 HTTP 重定向到 HTTPS(谷歌建議使用 301 跳轉)
- 你可以在打開某個網頁之前要求登錄,於是做一個 302 跳轉到登錄頁面;
- 你可以將一些已經過時的網頁進行 301 跳轉到新的網頁;
- 比如我將一些之前不太規範的博客 URL 重定向到統一的格式;
- 你可以在遷移服務的時候臨時做一個 302 跳轉。
小心緩存
請注意,301 重定向會被瀏覽器緩存。也就是說如果你重定向到了一個錯誤的網址,那麼再次訪問的話瀏覽器將直接訪問這個錯誤的網址。如果希望瀏覽器停止重定向到這個錯誤的網址,需要清除瀏覽器的緩存。所以使用 301 的時候需要謹慎一些。
參考資料