在HTTP的语义中,重定向一般指的是服务端通过返回一个状态码为3XX的响应促使客户端像另一个地址再次发起请求,本章将此称为“客户端重定向“。既然有客户端重定向,自然就有服务端重定向,本章所谓的服务端重定向指的是在服务端通过改变请求路径将请求导向另一个终结点。ASP.NET下的重定向是通过RewriteMiddleware中间件实现的。(本文提供的示例演示已经同步到《ASP.NET Core 6框架揭秘-实例演示版》)

[S2501]客户端重定向 (源代码

[S2502]服务端重定向 (源代码

[S2503]采用IIS重写规则实现重定向(源代码

[S2504]采用Apache重写规则实现重定向(源代码

[S2505]基于HTTPS终结点的重定向(源代码

[S2501]客户端重定向

我们可以为RewriteMiddleware中间件定义客户端重定向规则使之返回一个Location报头指向重定向地址的3XX响应。客户端(比如浏览器)在接收到这样的响应后会根据状态码约定的语义向重定向地址重新发起请求,我们将这种由客户端对新的地址重新请求的方式称为“客户端重定向”。

下面演示的这个例子会将请求路径以“foo/**”为前缀的请求重定向到新的路径“/bar/**”。如代码片段所示,我们通过调用UseRewriter扩展方法注册了RewriteMiddleware中间件,该方法会将对应的RewriteOptions配置选项作为参数。我们直接调用构造函数创建的这个RewriteOptions对象,并调用其AddRedirect扩展方法添加了一个重定向规则,该方法定义了两个参数,前者(“^/foo/(.*)”)代表参与重定向的原始路径模式(正则表达式),后者(“baz/$1”)表示重定向目标地址模板,占位符“$1”表示在进行正则匹配时产生的首段捕获内容(前缀“foo/”后面的部分)。请求的URL会作为响应的内容。

using Microsoft.AspNetCore.Rewrite;

var app = WebApplication.Create();
var options = new RewriteOptions().AddRedirect("^foo/(.*)", "bar/$1");
app.UseRewriter(options);
app.MapGet("/{**foobar}", (HttpRequest request) =>$"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}");
app.Run();

演示程序注册了一个采用“/{**foobar}”路由模板的终结点,请求URL直接作为该终结点的响应内容。演示程序启动之后,所有路径以“/foo”为前缀的请求都会自动重定向到以“/bar”为前缀的地址。如果请求路径被设置为“/foo/abc/123”,最终将会被重定向到图1所示的“/bar/abc/123”路径下。

图1 客户端重定向

整个过程涉及HTTP报文交换更能体现客户端重定向的本质。如下所示的是整个过程涉及的两次报文交换,我们可以看出服务端第一次返回的是状态码为302的响应,根据映射规则生成的重定向地址体现在Location报头上。

GET http://localhost:5000/foo/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 302 Found
Content-Length: 0
Date: Wed, 22 Sep 2021 13:34:17 GMT
Server: Kestrel
Location: /bar/abc/123
GET http://localhost:5000/bar/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 200 OK
Date: Wed, 22 Sep 2021 13:34:17 GMT
Server: Kestrel
Content-Length: 33 http://localhost:5000/bar/abc/123

[S2502]服务端重定向

服务端重定向会在服务端通过重写请求路径的方式将请求重定向到新的终结点。对于前面演示的程序来说,我们只需要对它做简单的修改就能切换到服务端重定向。如下面的代码片段所示,在RewriteOptions对象被创建后,我们调用它的另一个AddRewrite扩展方法注册了一条服务端重定向(URL重写)规则,原始请求路径的正则表达式和重定向路径均保持不变。

using Microsoft.AspNetCore.Rewrite;

var app = WebApplication.Create();
var options = new RewriteOptions().
.AddRewrite(regex: "^foo/(.*)", replacement: "bar/$1", skipRemainingRules: true);
app.UseRewriter(options);
app.MapGet("/{**foobar}", (HttpRequest request) =>
$"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}");
app.Run();

改动的程序启动后,如果利用浏览器采用相同的路径(“/foo/abc/123”)对站点发起请求,我们会得到如图2所示的相同的响应内容。由于这次采用的是服务端重定向,整个过程只会涉及一次报文交换,所以浏览器的请求地址不会改变。

图2 服务端重定向

[S2503]采用IIS重写规则实现重定向

重定向是绝大部分Web服务器(比如IIS、Apache和Nginx等)都会提供的功能,但是不同的服务器类型针对重定向规则具有不同的定义方式。IIS中的重定向被称为“URL重写”,具体的URL重写规则采用XML格式进行定义,RewriteMiddleware中间件对它提供了原生的支持。我们将URL重写规则以如下的方式定义在创建的rewrite.xml文件中,并将该文件保存在演示项目的根目录下。

<rewrite>
<rules>
<rule name="foo">
<match url="^foo/(.*)" />
<action type="Redirect" url="baz/{R:1}" />
</rule>
<rule name="bar">
<match url="^bar/(.*)" />
<action type="Rewrite" url="baz/{R:1}" />
</rule>
</rules>
</rewrite>

如上所示的XML文件定义了两条指向目标地址“baz/{R:1}”的规则,这里的占位符“{R:1}”和前面定义的“$1”一样,都表示针对初始请求路径进行正则匹配时得到的第一段捕获内容。两条规则用来匹配原始路径的正则表达式分别定义为“^foo/(.*)”和“^bar/(.*)”。它们采用的Action类型也不相同,前者为“Redirect”,表示客户端重定向;后者为“Rewrite”,表示服务端重定向。

为了将采用XML文件定义的IIS重定向规则应用到演示程序中,我们对演示程序如下的修改。如代码片段所示,在RewriteOptions对象被创建出来后,我们调用了它的AddIISUrlRewrite扩展方法添加了IIS URL重写规则,该方法的两个参数分别表示用来读取规则文件的IFileProvider对象和规则文件针对该对象的路径。由于规则文件存储与项目根目录下,这也是ASP.NET应用“内容根目录”所在的位置,所以我们可以使用内容根目录对应的IFileProvider对象。

using Microsoft.AspNetCore.Rewrite;

var app = WebApplication.Create();
var options = new RewriteOptions().AddIISUrlRewrite(fileProvider: app.Environment.ContentRootFileProvider, filePath: "rewrite.xml");
app.UseRewriter(options);
app.MapGet("/{**foobar}", (HttpRequest request) =>$"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}");
app.Run();

改动的程序启动之后,我们针对添加的两条重定向规则发送了对应的请求,它们采用的请求路径分别为“/foo/abc/123”和“/bar/abc/123”。从图3所示的输出可以看出,这两个请求均被重定向到相同的目标路径“/baz/abc/123”。

图3 IIS重定向规则

由于发送的两个请求分别采用客户端和服务端重定向方式导向新的地址,所以浏览器针对前者显示的是重定向后的地址,对于后者则显示原始的地址。整个过程涉及到的如下三次报文交互更能说明两种重定向方式的差异,从报文内容我们可以进一步看出第一次采用的是响应状态码为301的永久重定向。

GET http://localhost:5000/foo/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 301 Moved Permanently
Content-Length: 0
Date: Wed, 22 Sep 2021 23:26:02 GMT
Server: Kestrel
Location: /baz/abc/123
GET http://localhost:5000/baz/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 200 OK
Date: Wed, 22 Sep 2021 23:26:02 GMT
Server: Kestrel
Content-Length: 33 http://localhost:5000/baz/abc/123
GET http://localhost:5000/bar/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 200 OK
Date: Wed, 22 Sep 2021 23:26:26 GMT
Server: Kestrel
Content-Length: 33 http://localhost:5000/baz/abc/123

[S2504]采用Apache重写规则实现重定向

上面我们演示了RewriteMiddleware中间件针对IIS重定向规则的支持,实际上该中间件还支持Apache的重定向模块mod_rewriter所采用的重定向规则定义形式,我们照例来做一个简单的演示。我们在项目根目录下添加了一个名为rewrite.config的配置文件,并在其中定义了如下两条重定向规则。

RewriteRule ^/foo/(.*) /baz/$1 [R=307]
RewriteRule ^/bar/(.*) - [F]

上面第一条规则利用R这个Flag将路径与正则表达式“^/foo/(.*)”相匹配的请求以重定向到新的路径“/baz/$1”,具体采用的是针对状态码307的临时客户端重定向。对于其路径与正则表达式“^/bar/(.*)”相匹配的请求,我们将它视为未经授权授权的请求,所以对应的规则采用F(Forbidden)这个Flag。为了让演示程序采用上述这个配置文件定义的Apache重定向规则,我们只需要按照如下的方式调用RewriteOptions 对象的AddApacheModRewrite扩展方法就可以了。

using Microsoft.AspNetCore.Rewrite;

var app = WebApplication.Create();
var options = new RewriteOptions().AddApacheModRewrite(fileProvider: app.Environment.ContentRootFileProvider, filePath: "rewrite.config");
app.UseRewriter(options);
app.MapGet("/{**foobar}", (HttpRequest request) =>$"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}");
app.Run();

改动的程序启动之后,我们针对添加的两条重定向规则发送了对应的请求,它们采用的请求路径分别为“/foo/abc/123”和“/bar/abc/123”。从图4所示的输出可以看出,第一个请求均被重定向到相同的目标路径“/baz/abc/123”,第二个请求返回一个状态码为403的响应。

图4Apache mod­_rewrite重定向规则

如下所示的是整个过程涉及到的三次报文交换。我们可以看出第一次请求得到的响应状态码正式我们在规则中显式设置的307。第二个请求由于被视为权限不足,服务端直接返回一个状态为“403 Forbidden”的响应。

GET http://localhost:5000/foo/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 307 Temporary Redirect
Content-Length: 0
Date: Wed, 22 Sep 2021 23:56:26 GMT
Server: Kestrel
Location: /baz/abc/123
GET http://localhost:5000/baz/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 200 OK
Date: Wed, 22 Sep 2021 23:56:26 GMT
Server: Kestrel
Content-Length: 33
GET http://localhost:5000/bar/abc/123 HTTP/1.1
Host: localhost:5000 HTTP/1.1 403 Forbidden
Content-Length: 0
Date: Wed, 22 Sep 2021 23:56:33 GMT
Server: Kestrel

[S2505]基于HTTPS终结点的重定向

将针对HTTP请求重定向到对应HTTPS终结点是一种常见的重定向场景。如下所示的演示针对路径“/foo”和“/bar”注册了两个终结点,它们均由注册的两个中间件构建的RequestDelegate委托作为处理器,其中一个就是调用UseRewriter扩展方法注册的RewriteMiddleware中间件,另一个中间件则是通过调用Run扩展方法注册的,后者依然将最终请求的URL作为响应的内容。

using Microsoft.AspNetCore.Rewrite;

var app = WebApplication.Create();
app.MapGet("/foo", CreateHandler(app, 302));
app.MapGet("/bar", CreateHandler(app, 307));
app.Run(); static RequestDelegate CreateHandler(IEndpointRouteBuilder endpoints, int statusCode)
{
var app = endpoints.CreateApplicationBuilder();
app
.UseRewriter(new RewriteOptions().AddRedirectToHttps(statusCode, 5001))
.Run(httpContext => {
var request = httpContext.Request;
var address =
$"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}";
return httpContext.Response.WriteAsync(address);
});
return app.Build();
}

两个终结点的处理器通过本地方法CreateHandler创建出来的。该方法调用当前WebApplication对象的CreateApplicationBuilder方法创建了一个新的IApplicationBuilder对象,并调用后者的UseRewriter扩展方法注册了RewriteMiddleware中间件。我们为该中间件提供的HTTPS重定向规则是通过调用RewriteOptions对象的AddRedirectToHttps扩展方法定义的,该方法时指定了重定向响应采用的状态码(302和307)和HTTPS终结点采用的端口号。改动的程序启动之后,针对两个终结点的HTTP请求(“http://localhost:5000/foo”和“http://localhost:5000/bar”)均以图5所示的形式被重定向到了对应的HTTPS终结点。

图5 HTTPS重定向

整个过程涉及到如下四次报文交换,我们可以看出我们通过调用AddRedirectToHttps扩展方法定义的规则采用的是客户端重定向。重定向响应采用了我们设置的状态码,分别是“302 Found”和“307 Temporary Redirect”。

GET http://localhost:5000/foo HTTP/1.1
Host: localhost:5000 HTTP/1.1 302 Found
Content-Length: 0
Date: Thu, 23 Sep 2021 12:10:51 GMT
Server: Kestrel
Location: https://localhost:5001/foo
GET https://localhost:5001/foo HTTP/1.1
Host: localhost:5001 HTTP/1.1 200 OK
Date: Thu, 23 Sep 2021 12:10:51 GMT
Server: Kestrel
Content-Length: 26 https://localhost:5001/foo
GET http://localhost:5000/bar HTTP/1.1
Host: localhost:5000 HTTP/1.1 307 Temporary Redirect
Content-Length: 0
Date: Thu, 23 Sep 2021 12:10:57 GMT
Server: Kestrel
Location: https://localhost:5001/bar
GET https://localhost:5001/bar HTTP/1.1
Host: localhost:5001 HTTP/1.1 200 OK
Date: Thu, 23 Sep 2021 12:10:57 GMT
Server: Kestrel
Content-Length: 26 https://localhost:5001/bar

ASP.NET Core 6框架揭秘实例演示[37]:重定向的N种实现方式的更多相关文章

  1. ASP.NET Core 6框架揭秘实例演示[24]:中间件的多种定义方式

    ASP.NET Core的请求处理管道由一个服务器和一组中间件组成,位于 "龙头" 的服务器负责请求的监听.接收.分发和最终的响应,针对请求的处理由后续的中间件来完成.中间件最终体 ...

  2. ASP.NET Core 6框架揭秘实例演示[07]:文件系统

    ASP.NET Core应用具有很多读取文件的场景,如读取配置文件.静态Web资源文件(如CSS.JavaScript和图片文件等).MVC应用的视图文件,以及直接编译到程序集中的内嵌资源文件.这些文 ...

  3. ASP.NET Core 6框架揭秘实例演示[08]:配置的基本编程模式

    .NET的配置支持多样化的数据源,我们可以采用内存的变量.环境变量.命令行参数.以及各种格式的配置文件作为配置的数据来源.在对配置系统进行系统介绍之前,我们通过几个简单的实例演示一下如何将具有不同来源 ...

  4. ASP.NET Core 6框架揭秘实例演示[09]:配置绑定

    我们倾向于将IConfiguration对象转换成一个具体的对象,以面向对象的方式来使用配置,我们将这个转换过程称为配置绑定.除了将配置树叶子节点配置节的绑定为某种标量对象外,我们还可以直接将一个配置 ...

  5. ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式

    依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...

  6. ASP.NET Core 6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式

    在整个软件开发维护生命周期内,最难的不是如何将软件系统开发出来,而是在系统上线之后及时解决遇到的问题.一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根 ...

  7. ASP.NET Core 6框架揭秘实例演示[12]:诊断跟踪的进阶用法

    一个好的程序员能够在系统出现问题之后马上定位错误的根源并找到正确的解决方案,一个更好的程序员能够根据当前的运行状态预知未来可能发生的问题,并将问题扼杀在摇篮中.诊断跟踪能够帮助我们有效地纠错和排错&l ...

  8. ASP.NET Core 6框架揭秘实例演示[13]:日志的基本编程模式[上篇]

    <诊断跟踪的几种基本编程方式>介绍了四种常用的诊断日志框架.其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net.NLog和Serilog 等.虽然这些框 ...

  9. ASP.NET Core 6框架揭秘实例演示[14]:日志的进阶用法

    为了对各种日志框架进行整合,微软创建了一个用来提供统一的日志编程模式的日志框架.<日志的基本编程模式>以实例演示的方式介绍了日志的基本编程模式,现在我们来补充几种"进阶" ...

  10. ASP.NET Core 6框架揭秘实例演示[15]:针对控制台的日志输出

    针对控制台的ILogger实现类型为ConsoleLogger,对应的ILoggerProvider实现类型为ConsoleLoggerProvider,这两个类型都定义在 NuGet包"M ...

随机推荐

  1. 机器学习系列入门系列[七]:基于英雄联盟数据集的LightGBM的分类预测

    1. 机器学习系列入门系列[七]:基于英雄联盟数据集的LightGBM的分类预测 1.1 LightGBM原理简介 LightGBM是2017年由微软推出的可扩展机器学习系统,是微软旗下DMKT的一个 ...

  2. Django笔记十五之in查询及date日期相关过滤操作

    这一篇介绍关于范围,日期的筛选 in range date year week weekday quarter hour 1.in in 对应于 MySQL 中的 in 操作,可以接受数组.元组等类型 ...

  3. 全网最详细 二进制 k8s v1.25.x文档

    二进制安装k8s v1.25.0 IPv4/IPv6双栈 Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes(k8s)二进制高可用安装部署,支持IPv4+IPv6双栈 ...

  4. 【入门排坑】Windows之间使用OpenSSH的ssh免密登录,排坑

    安装 安装OpenSSH 需要安装OpenSSH客户端和服务器,win10自带客户端,我们安装服务器即可. 设置 -- 应用 -- 可选功能 -- 添加 -- 添加 OpenSSH 服务器 配置 公钥 ...

  5. [Linux/Apache Http]Apache Http(d)服务访问时报: 403 Forbidden You don't have permission to access /cdh/ on this server.

    1 问题描述 http错误代码403:403 Forbidden 资源不可用.服务器理解客户的请求,但拒绝处理它.通常由于服务器上文件或目录的权限设置导致. 2 解决思路 胜利的果实: 确保关闭sel ...

  6. windows 10 家庭版安装Docker和portainer汉化版

    目录 前景提要 存在问题 一.Docker的windows版本安装 1.官网下载: https://www.docker.com/(没有本文问题的直接下载就好.) 2.本文是找到其他版本的Docker ...

  7. 逍遥自在学C语言 | 位运算符^的高级用法

    前言 在上一篇文章中,我们介绍了|运算符的高级用法,本篇文章,我们将介绍^ 运算符的一些高级用法. 一.人物简介 第一位闪亮登场,有请今后会一直教我们C语言的老师 -- 自在. 第二位上场的是和我们一 ...

  8. CI框架内置分页代码

    Controller 控制器代码 <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Welc ...

  9. AutoGPT目前只是成功学大师GPT版

    一大波韭菜被收割了 最近很多人在交流对于AutoGPT的震惊和激动.AutoGPT是一个开源的应用程序,展示了GPT-4语言模型的能力.这个程序由GPT-4驱动,自主地开发和管理业务,以增加净值.它是 ...

  10. TENGSHE-OS-渗透测试系统-win11版

    下载ISO文件 创建新的虚拟机 VM17 已支持直接创建 win11 x64 稍后安装系统 选中win11 修改路径 win11需要设置8位加密密码 勾选安全引导 根据自身情况选择 默认即可 150G ...