ASP.NET Core URL Rewrite中间件
URL重写是基于一个或多个预置规则修改请求URL的行为。URL重写在资源位置和访问地址之间创建了一种抽象,这样二者之间就减少了紧密的联系。URL重写有多种适用的场景:
- 临时或永久移动或替换服务器资源,同时为这些资源保持稳定的访问
- 为不同应用程序或同一个应用程序的不同区域的拆分请求处理
- 根据请求移除、添加、重新组织URL段(segment)
- SEO优化
- 允许使用友好的公共URL来帮助人们通过链接预测找到内容
- 将不安全的请求重定向到安全端点
- 图片防盗链
可以通过多种方式定义改变URL的规则,包括正则表达式、Apache mod_rewrite模块规则、IIS重写模块规则和自定义规则逻辑。本文介绍URL重写及说明如何在ASP.NET Core应用中使用URL重写中间件。
注意:URL重写可能会降低应用的性能,您应该尽可能的限制规则的数量和规则的复杂性。
URL重定向和URL重写
从字面意思上看URL重定向和URL重写的差异并不明显,但二者在提供资源给客户端方面都有重要意义。ASP.NET Core的URL重写中间件能够同时满足二者的需求。URL重定向是客户端操作,指示客户端在另一个地址访问资源,需要额外往返服务器。当客户端对资源发出请求时,返回到客户端的重定向URL将显示在浏览器的地址栏中。例如/resource被重定向到/different-resource时:客户端请求/resource,服务端响应客户端应在/different-resource获取资源,其响应的状态码会指示重定向是临时的还是永久的,然后客户端会向/different-resource发送一个新请求获取资源。
将请求重定向到其他URL时,可以指定重定向是永久还是临时。301(Moved Permanently)状态代码用于表明资源具有新的永久URL,并且希望客户端将来对该资源的所有请求都应使用新URL。当收到301状态码时客户端可以缓存响应。302(Found)状态码用于临时重定向,所以客户端不应该存储和重用该URL。状态码的含义请参考这里。URL重写是服务器端操作,用于从不同的资源地址提供资源。URL重写不需要额外的往返服务器,并且重写后的URL不会返回给客户端,也不会出现在客户端的地址栏中。当/resource被重写为/different-resource时:客户端请求/resource,服务端在内部从/different-resource获取资源并响应给客户端。尽管客户端也许可以从重写后的URL处获取资源,但客户端并不会收到资源存在于重写后URL的通知。

何时使用URL重写中间件
当无法在Windows Server上使用IIS重写模块、Apache服务器上的Apache mod_rewrite模块、Nginx上的URL重写或应用程序托管在HTTP.sys服务器(以前称为WebListener)上时,请使用URL重写中间件。推荐在IIS,Apache或Nginx中使用基于服务器的URL重写技术的主要原因是中间件不支持这些模块的全部功能,并且中间件的性能可能无法达到这些模块的性能。但是,这些服务器重写模块的某些功能不适用于ASP.NET Core项目,例如IIS Rewrite模块的IsFile和IsDirectory。在这些情况下,请改用中间件。
包引用
要在项目中使用URL重写中间件,请添加Microsoft.AspNetCore.Rewrite包的引用。该功能适用于ASP.NET Core 1.1或更高版本的应用程序。
配置重写及重定向规则
通过RewriteOptions类实例的扩展方法来建立URL重写和重定向规则,按照你希望处理的顺序将这些规则链接起来,然后通过使用app.UseRewriter(options)将URL重写选项传递到请求管道,以下是几种重写、重定向的配置代码,后面会针对每种配置单独解释:
public void Configure(IApplicationBuilder app)
{
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1")
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
skipRemainingRules: true)
.AddApacheModRewrite(apacheModRewriteStreamReader)
.AddIISUrlRewrite(iisUrlRewriteStreamReader)
.Add(MethodRules.RedirectXMLRequests)
.Add(new RedirectImageRequests(".png", "/png-images"))
.Add(new RedirectImageRequests(".jpg", "/jpg-images")); app.UseRewriter(options);
} app.Run(context => context.Response.WriteAsync(
$"Rewritten or Redirected Url: " +
$"{context.Request.Path + context.Request.QueryString}"));
}
URL重定向
使用AddRedirect方法重定向请求,第一个参数为匹配请求URL的正则表达式,第二个参数为替换的文本,第三个参数(如果存在)指定状态码,如果未指定状态码,默认为302(Found)。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRedirect("redirect-rule/(.*)", "redirected/$1"); app.UseRewriter(options);
}
打开浏览器的开发者工具,向/redirect-rule/1234/5678发送一个请求。重定向规则中的正则表达式将匹配请求路径,将路径替换为/redirected/1234/5678,服务端将重定向URL和302(Found)状态代码发送回客户端。客户端基于该URL发送新请求并将该URL显示到地址栏中,然后客户端收到一个200(OK)的响应。
警告:新建重定向规则时一定要谨慎,重定向规则将会对应用每一个请求都进行匹配,包括重定向后的URL。所以很容易不小心创建一个无限重定向循环。
发送一个请求:/redirect-rule/1234/5678,响应如下图:

重定向规则中正则表达式括号内的部分称为捕获组,表达式中点(.)的含义是匹配任何字符,星号(*)表示匹配之前的字符零次或者多次。因此,URL中最后两段/123/5678被(.*)捕获组所捕获,URL中位于redirect-rule/之后的任何值都将会被该组捕获。
在替换字符串中,捕获组将捕获的内容注入到($n)符号所在位置,其中$后的数字n代表捕获的序列号。第一个捕获组是$1,第二个是$2,以此类推。在上面的例子中,重定向规则中的正则表达式只有一个捕获组,所以替换字符串中只有一个$1,最终/redirect-rule/1234/5678被替换为/redirect-rule/1234/5678。
URL重定向到安全站点
可使用AddRedirectToHttps方法将不安全的请求重定向到具有安全HTTPS协议的同一主机和路径,如果未提供状态码参数,中间件将使用默认值302(Found)。如果未提供端口号参数,中间件使用默认值null,这意味着客户端将使用https协议同时从443端口访问资源,下面的代码片段演示如何将重定向状态码设为301(Moved Permanently),同时将端口设为5001:
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions().AddRedirectToHttps(, ); app.UseRewriter(options);
}
也可以使用AddRedirectToHttpsPermanent方法将不安全的请求重定向到具有安全HTTPS协议的同一主机和路径(端口443上的https://)。中间件将响应状态码设置为301(Moved Permanently)。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions().AddRedirectToHttpsPermanent(); app.UseRewriter(options);
}
注意:在不需要其他重定向规则的情况下重定向到HTTPS时,建议使用HTTPS重定向中间件。请参考这里
URL重写
可使用AddRewrite方法创建重写规则,第一个参数为匹配请求URL的正则表达式,第二个参数是替换字符串,第三个参数skipRemainingRules: {true|false},表示如果当前规则生效是否要跳过其它的重写规则。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", skipRemainingRules: true); app.UseRewriter(options);
}
}
发送一个请求:/rewrite-rule/1234/5678,重定向请求及响应如下图:

我们注意到正则表达式开头是字符^,它的含义是匹配需要从URL路径的开头开始。在之前重定向例子中,正则表达式的开头并没有字符^,因此,路径中redirect-rule/之前的任何字符都可以成功匹配。
| 路径 | 是否匹配 |
| /redirect-rule/1234/5678 | 是 |
| /my-cool-redirect-rule/1234/5678 | 是 |
| /anotherredirect-rule/1234/5678 | 是 |
在重写规则中,正则表达式^rewrite-rule/(\d+)/(\d+)仅匹配以rewrite-rule/开头的路径,请注意二者之间的区别:
| 路径 | 是否匹配 |
| /rewrite-rule/1234/5678 | 是 |
| /my-cool-rewrite-rule/1234/5678 | 否 |
| /anotherrewrite-rule/1234/5678 | 否 |
在正则表达式^rewrite-rule/(\d+)/(\d+)中有两个捕获组:(\d+)/(\d+),\d表示匹配一个数字,加号(+)表示匹配之前的字符1次或者多次。因此,匹配的URL必须包含一个数字,后跟一个正斜杠,后跟另一个数字。捕获的内容将会被分别注入到重写字符串中的$1和$2位置。所以请求URL/rewrite-rule/1234/5678将会被重写为/rewritten?var1=1234&var2=5678。如果原始请求中存在查询字符串,则在重写URL时会保留该查询字符串。URL重写不会有额外的服务器往返。如果资源存在,服务端获取资源内容并返回给客户端200(OK)状态码。因为客户端没有被重定向,所以浏览器地址栏中的地址不会改变。就客户端而言,是感知不到URL重写的。
注意:尽可能使用skipRemainingRules:true参数,因为匹配规则是一个昂贵的过程并增加了应用程序响应时间。为了更快的响应,请考虑以下建议:
- 将重写规则排序:从最常匹配的规则到最不常匹配的规则
- 规则匹配成功之后跳过剩余的规则
使用Apache mod_rewrite规则
使用AddApacheModRewrite方法应用Apache mod_rewrite规则,请确保规则文件已随应用程序部署至服务器。了解更多关于Apache mod_rewrite规则,请参考这里
public void Configure(IApplicationBuilder app)
{
//StreamReader用于从ApacheModRewrite.txt规则文件中读取规则。
using (StreamReader apacheModRewriteStreamReader =
File.OpenText("ApacheModRewrite.txt"))
{
var options = new RewriteOptions()
.AddApacheModRewrite(apacheModRewriteStreamReader); app.UseRewriter(options);
}
}
以下为ApacheModRewrite.txt的内容:
# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$ [L,R=]
示例应用程序将来自/apache-mod-rules-redirect/(.\*)的请求重定向到/redirected?id=$1,响应码为302(Found)。
中间件支持以下Apache mod_rewrite服务器变量:
- CONN_REMOTE_ADDR
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_FORWARDED
- HTTP_HOST
- HTTP_REFERER
- HTTP_USER_AGENT
- HTTPS
- IPV6
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_METHOD
- REQUEST_SCHEME
- REQUEST_URI
- SCRIPT_FILENAME
- SERVER_ADDR
- SERVER_PORT
- SERVER_PROTOCOL
- TIME
- TIME_DAY
- TIME_HOUR
- TIME_MIN
- TIME_MON
- TIME_SEC
- TIME_WDAY
- TIME_YEAR
使用IIS URL重写模块规则
使用AddIISUrlRewrite方法应用IIS URL重写规则,请确保规则文件已随应用程序部署至服务器。在Windows Server IIS上运行时,不要让中间件直接使用web.config文件,规格文件应该存储于web.config之外,以避免和IIS重写模块冲突。了解更多关于IIS重写模块的规则,请参考这里和这里。
public void Configure(IApplicationBuilder app)
{
//StreamReader用于从IISUrlRewrite.xml规则文件中读取规则
using (StreamReader iisUrlRewriteStreamReader =
File.OpenText("IISUrlRewrite.xml"))
{
var options = new RewriteOptions()
.AddIISUrlRewrite(iisUrlRewriteStreamReader); app.UseRewriter(options);
}
}
以下为IISUrlRewrite.xml的内容:
<rewrite>
<rules>
<rule name="Rewrite segment to id querystring" stopProcessing="true">
<match url="^iis-rules-rewrite/(.*)$" />
<action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
</rule>
</rules>
</rewrite>
示例应用程序将来自/iis-rules-rewrite/(.*)的请求重写为/rewritten?id=$1,响应码为200(OK)。
ASP.NET Core 2.x发布的中间件不支持以下IIS URL重写模块功能:
- Outbound Rules
- Custom Server Variables
- Wildcards
- LogRewrittenUrl
中间件支持以下IIS URL重写模块服务器变量:
- CONTENT_LENGTH
- CONTENT_TYPE
- HTTP_ACCEPT
- HTTP_CONNECTION
- HTTP_COOKIE
- HTTP_HOST
- HTTP_REFERER
- HTTP_URL
- HTTP_USER_AGENT
- HTTPS
- LOCAL_ADDR
- QUERY_STRING
- REMOTE_ADDR
- REMOTE_PORT
- REQUEST_FILENAME
- REQUEST_URI
注意:可以通过PhysicalFileProvider类获取IFileProvider。这种方法可以为重写规则文件的位置提供更大的灵活性。
PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
基于方法的规则
使用Add(Action<RewriteContext> applyRule)在方法中实现自己的规则逻辑,RewriteContext公开HttpContext以方便在方法中使用,而context.Result决定了如何进行后续的管道处理。如下表:
| context.Result | 行为 |
| RuleResult.ContinueRules(默认行为) | 继续应用后续规则 |
| RuleResult.EndResponse | 停止应用规则并发送响应 |
| RuleResult.SkipRemainingRules | 停止应用规则并发送上下文(HttpContext)至下个中间件 |
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.Add(MethodRules.RedirectXMLRequests); app.UseRewriter(options);
} //自定义的规则方法
public static void RedirectXMLRequests(RewriteContext context)
{
var request = context.HttpContext.Request; // Because we're redirecting back to the same app, stop
// processing if the request has already been redirected
if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
{
return;
} if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
"/xmlfiles" + request.Path + request.QueryString;
}
}
示例应用程序演示了将.xml结尾的请求路径重定向的自定义逻辑方法。如果对/file.xml发出请求,则会将其重定向到/xmlfiles/file.xml。响应码被设置为301 (Moved Permanently)。对于重定向来说,你必须显式指定响应的状态码,否则响应码将被默认为200(OK)且客户端也不会发生重定向。
发送一个请求:/file.xml,响应如下图:

基于IRule接口的规则
使用Add(IRule)在从IRule派生的类中实现您自己的规则逻辑。使用IRule的方式比使用基于方法的规则方法具有更好的灵活性,派生类可以包含构造函数,您可以在其中传递ApplyRule方法的参数。
public void Configure(IApplicationBuilder app)
{
var options = new RewriteOptions()
.Add(new RedirectImageRequests(".jpg", "/jpg-images")); app.UseRewriter(options);
}
public class RedirectImageRequests : IRule
{
private readonly string _extension;
private readonly PathString _newPath; public RedirectImageRequests(string extension, string newPath)
{
//此处省略了参数校验 _extension = extension;
_newPath = new PathString(newPath);
} public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
if (request.Path.StartsWithSegments(new PathString(_newPath)))
{
return;
} if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
{
var response = context.HttpContext.Response;
response.StatusCode = StatusCodes.Status301MovedPermanently;
context.Result = RuleResult.EndResponse;
response.Headers[HeaderNames.Location] =
_newPath + request.Path + request.QueryString;
}
}
}
发送一个请求:/image.png,响应内容如下:

尾语
本来主要内容来自于微软官方英文文档,点击这里查看原文,在翻译过程中,对有些句式和词汇进行了加工,以期望更加流畅和符合我们的阅读习惯,其中应该有不少不得体的地方,还请各位大神见谅并指出,如有一丝帮助,万分荣幸。
ASP.NET Core URL Rewrite中间件的更多相关文章
- Asp.Net Core 通过自定义中间件防止图片盗链的实例(转)
一.原理 要实现防盗链,我们就必须先理解盗链的实现原理,提到防盗链的实现原理就不得不从HTTP协议说起,在HTTP协议中,有一个表头字段叫referer,采用URL的格式来表示从哪儿链接到当前的网页或 ...
- 在Asp.net Core中使用中间件来管理websocket
介绍 ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理.但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容. ...
- ASP.NET Core静态文件中间件[1]: 搭建文件服务器
虽然ASP.NET Core是一款"动态"的Web服务端框架,但是由它接收并处理的大部分是针对静态文件的请求,最常见的是开发Web站点使用的3种静态文件(JavaScript脚本. ...
- ASP.NET Core 3.1 中间件
参考微软官方文档 : https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1 ...
- ASP.NET Core错误处理中间件[1]: 呈现错误信息
NuGet包"Microsoft.AspNetCore.Diagnostics"中提供了几个与异常处理相关的中间件.当ASP.NET Core应用在处理请求过程中出现错误时,我们可 ...
- ASP.NET Core 中的中间件
前言 由于是第一次写博客,如果您看到此文章,希望大家抱着找错误.批判的心态来看. sky! 何为中间件? 在 ASP.NET Framework 中应该都知道请求管道.可参考:浅谈 ASP.NET ...
- asp.net core 自定义异常处理中间件
asp.net core 自定义异常处理中间件 Intro 在 asp.net core 中全局异常处理,有时候可能不能满足我们的需要,可能就需要自己自定义一个中间件处理了,最近遇到一个问题,有一些异 ...
- 给 asp.net core 写个中间件来记录接口耗时
给 asp.net core 写个中间件来记录接口耗时 Intro 写接口的难免会遇到别人说接口比较慢,到底慢多少,一个接口服务器处理究竟花了多长时间,如果能有具体的数字来记录每个接口耗时多少,别人再 ...
- ASP.NET Core系列:中间件
1. 概述 ASP.NET Core中的中间件是嵌入到应用管道中用于处理请求和响应的一段代码. 2. 使用 IApplicationBuilder 创建中间件管道 2.1 匿名函数 使用Run, Ma ...
随机推荐
- Python中的列表生成式和多层表达式
Python中的列表生成式和多层表达式 如何生成[1x1, 2x2, 3x3, ..., 10x10]的列表? L=[]; ,): L.append(x*x) print L print (" ...
- effective javascript 学习心得
第2条:理解JavaScript的浮点数 1.js数字只有一种类型 2.见代码 /** * Created by Administrator on 2017/7/2. */ console.log(& ...
- C# unsafe模式内存操作深入探索
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Run ...
- Spark scala和java的api使用
1.利用scala语言开发spark的worcount程序(本地运行) package com.zy.spark import org.apache.spark.rdd.RDD import org. ...
- 111. Minimum Depth of Binary Tree (Tree; DFS)
Given a binary tree, find its minimum depth. The minimum depth is the number of nodes along the shor ...
- MYSQL 测试常用语句使用技巧
终于有时间可以整理一下工作中常用的sql语句,基本的sql语句及增删改查就不说了.对于测试而言,经常用到的还是造数据,取随机数据和查询.比如造数据时,为了确保数据真实性,可能时间是随机的,用户是随机 ...
- Spring Data JPA + layui的前台分页插件layPage实现页面的分页
一.后台代码: 1.1 controller层代码 @RequestMapping("/xxxxxx") public String showInformationCode(Str ...
- HTML5框架、背景和实体
-----------------siwuxie095 HTML5 框架 1.框架标签 <frame> ...
- Zookeeper使用--开源客户端
一.ZkClient ZkClient是在Zookeeper原生API接口之上进行了包装,是一个更易用的Zookeeper客户端,其内部还实现了诸如Session超时重连.Watcher反复注册等功能 ...
- JMS 之 Active MQ的安全机制
一.认证 认证(Authentication):验证某个实体或者用户是否有权限访问受保护资源. MQ提供两种插件用于权限认证:(一).Simple authentication plug-in:直接把 ...