4

【ASP.NET Core】URL重写 - 东邪独孤

 1 year ago
source link: https://www.cnblogs.com/tcjiaan/p/16278164.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【ASP.NET Core】URL重写

今天老周和大伙伴们聊聊有关 Url Rewrite 的事情,翻译过来就是 URL 重写。

这里不得不提一下,URL重定向与重写的不同。

1、URL重定向是客户端(通常是浏览器)向服务器请求地址A,然后服务器要求重定向到B,返回状态码 301 或 302 给客户端,并且夹带一个 Location 的标头,其值表示要重定向的目标 URL,即B;随后客户端再用B向服务器发起请求,若成功,服务器返回内容并夹带状态码 200。

2、URL重写只在服务器上转换URL,当客户端请求地址A进入服务器后,服务器自行处理并转向B。最后返回B地址的内容,夹带状态码 200。此过程只在服务器上发生,不需要与客户端进行多次通信。因此浏览器地址栏中的URL也不会发生变化。

-------------------------------------------------- 超级分界线 ------------------------------------------------------

实现 URL 重写不需要向服务容器注册功能类,但可以在 Service 集合中配置 Options。你需要通过 RewriteOptions 对象来指定重定向的规则。定义规则的方法是实现 IRule 接口。此接口只有一个 ApplyRule 方法。在实现该方法时,根据需要修改 HttpContext.Request.Path 来设置新的 URL。

------------------------------------------------------------------------------------------------------------------------

下面咱们直接上示例,这里我写了一个简单的URL重写规则。

    public sealed class MyRule : IRule
    {
        public void ApplyRule(RewriteContext context)
        {
            var request = context.HttpContext.Request;
            var oldPath = request.Path;
            // 正则表达式来匹配
            var match = Regex.Match(oldPath, "/ft(\\d+)");
if(match == null || match.Success == false)
            {
                context.Result = RuleResult.ContinueRules;
                return;
            }
            // 找出匹配的分组
            var matchedval = match!.Groups[1].Value;
            // 新的URL
            PathString newPath = "/fight-action/" + matchedval;
            request.Path = newPath;  //修改为新URL
            context.Result = RuleResult.SkipRemainingRules;
        }
    }

这个规则是这样的:客户端请求 https://localhost/ft3,通过正则表达式,找出URL中的数值 3 ,然后改为新的 URL:https://localhost/fight-action/3。即

https://host/ft{数值}  ---->  https://host/fight-action/{数值}

为什么要重写 URL 呢,假设网站内部实现某功能的 URL 很长,很难看,很难记忆,用户看到它就想抽它。为了让用户觉得好记好看,在公开的前台 UI 或者 Web API 中使用一个更短更方便记忆的 URL。这又是为何呢?老周做个假设:假设你有个新闻系统,按照最初的开发设计,写一篇新闻的 URL 是 http://killer/news_manager/addnew。嗯,这个路径逻辑清晰、层次分明、表义明了(对开发人员来说,这样好维护,模块化);可对用户来说,他哪管你模块化还是分尸化,他就觉得这太长,不好记,也不好输入。行,咱们给来个 URL 重写,对外公开的 URL 变成 http://killer/addnews,而服务器内部还是转回原来的地址来处理,但客户端是毫无察觉的。

还有一种情况是网站修改了,后台的结构变了,API 的结构变了,可你懒得把所有前台 UI 改动。于是,你也可以写个 URL 重写规则,让旧 URL 自动转到新的 URL 上,同样客户端毫无察觉的(明修____,暗渡____)。

回到 ASP.NET Core 主体代码,这里为了节省体力和脑力,老周就不做 HTML 页了,直接 MapGet 代表每个页面。

// 模拟一些路径
app.MapGet("/", () => "燕双鹰战斗仪");
app.MapGet("/fight-action/{mode}", (int mode) => mode switch
{
    0     => "全自动扫射装载中……",
    1     => "装逼两分钟,开挂三小时",
    2     => "无限子弹碾压",
    3     => "我赌你的枪里没有子弹",
    4     => "脑浆警告",
    5     => "像你这样的人应该怎么改变,不会改变的,只有X",
    _     => "外挂已到期"
});

路径 /fight-action 后面一段是路由参数 mode,然后这个数值会随同参数 mode 传入lambda 表达式,内部根据 mode 的值返回不同的字符串。

使用 URL 重写我们不需要向服务容器添加依赖注入对象,直接在 HTTP 管线上以中间件方式 Use 一下即可。

app.UseRewriter();

不过,这个无参数调用是未添加任何自定义重写规则的,咱们有两种方法添加规则。

第一种方法:保持 UseRewriter 方法无参数调用,使用服务容器来 Config 一下 RewriteOptions 选项类。这个方法实际是在服务容器中生成了 IOptions<RewriteOptions> 对象,中间件类 RewriteMiddleware 的构造函数会注入这个选项类实例。

    // 以下为 .NET 源代码
    public RewriteMiddleware(
        RequestDelegate next,
        IWebHostEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory,
        IOptions<RewriteOptions> options)
    {
        // ……
        _next = next;
        _options = options.Value;
        _fileProvider = _options.StaticFileProvider ?? hostingEnvironment.WebRootFileProvider;
        _logger = loggerFactory.CreateLogger<RewriteMiddleware>();
    }

现在,咱们把刚刚写的 MyRule 规则配置一下。

var builder = WebApplication.CreateBuilder(args);
// 配置 rewrite options
builder.Services.Configure<RewriteOptions>(rwo =>
{
    rwo.Add(new MyRule());
});
var app = builder.Build();

第二种方法:在HTTP 管理线中调用 UseRewriter 方法前,直接 new 一个 RewriteOptions 实例,然后添加刚刚写的规则。最后调用 UseRewriter 方法的带参数版本,把 options 传给它即可。

RewriteOptions rwopt = new();
rwopt.Add(new MyRule());
app.UseRewriter(rwopt);

两种方法任选其一就可以了,不需要重复配置。

现在咱们运行一下示例。

默认打开主页是这样的。

367389-20220516185048012-2067552255.png

然后把 URL 改为 /ft3。

367389-20220516185229882-1455221404.png

URL 已经跳转,不过浏览器地址栏不会有变化,而且不会返回 301、302 给客户端,因为这个跳转过程是在服务器上完成的。

------------------------------------ 未知分界线 -------------------------------------------

其实,RewriteOptions 补充了一些扩展方法,使得咱们在简单重写URL(不需要特复杂的逻辑分析,用正则就能搞定的)时可以不去实现 IRule 接口。故,咱们这个示例可以改成这样更简洁的实现。

var builder = WebApplication.CreateBuilder(args);
// 配置 rewrite options
builder.Services.Configure<RewriteOptions>(rwo =>
{
    rwo.AddRewrite("ft(\\d+)", "fight-action/$1", true);
});
var app = builder.Build();

第一个参数是正则表达式,括号中表示捕捉为一个分组,第二个参数是新的 URL,其中“$1”表示引用正则表达式中捕捉的分组编号,咱们这个正则表达式中只有一个分组,即捕捉数值的 \d+,所以用 $1 引用它;如果有其他分组,那就依此类推,$2、$3、$4。它的意思就是引用捕捉到这个分组的值。如,匹配 ft2,捕捉到数值 2,然后替换 $1,使新的URL为 fight-action/2。注意在匹配和替换的 URL 都不用“/”开头,反正类库在处理时也会删除“/”的,所以我们就没必须加“/”。

再测试一下。

运行后,转到 /ft5,结果如下。

367389-20220516190628477-1696461603.png

好了,今天的节目就到此了,下次有空咱们再聊。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK