如果你有一些需要重定向网页 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 上,对其他功能很不利,所以中间件是最合适的方式。
重定向中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
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
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/// <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 的时候需要谨慎一些。
参考资料
本文会经常更新,请阅读原文: https://blog.walterlv.com/post/redirect-middleware-for-asp-dotnet.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。