IIS Url 重写:添加尾部斜杠、保留锚点和查询字符串
IIS Url Rewite: Add Trailing Slash, Preserve Anchors and Query Strings
我已经搜索了几个 SO post,但没有找到我要找的东西。它可能存在,但可能已经足够老了,不会出现在我面前。我发现 post (Nginx rewrite: add trailing slash, preserve anchors and query strings) 非常接近我的需要,但它的正则表达式解决方案不适用于 URL Rewrite for IIS,除非我做错了。
问题
我正在尝试在我的 url 路径末尾添加一个正斜杠 /
,同时还保留任何现有的查询字符串 ?
和锚点 #
.
期望的解决方案
基本上,这是每个问题的预期结果:
Entry: https://my.site.com/about
Result: https://my.site.com/about/
Entry: https://my.site.com/about?query=string
Result: https://my.site.com/about/?query=string
Entry: https://my.site.com/about#TestAnchor
Result: https://my.site.com/about/#TestAnchor
Entry: https://my.site.com/about?query=string#TestAnchor
Result: https://my.site.com/about/?query=string#TestAnchor
当前测试
我们当前的正则表达式忽略了查询字符串和锚点,但我现在想考虑它们。
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^.?]+[^.?/])$" />
<action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
我还测试了另一个正则表达式,但它仅在 url 包含查询字符串和锚点时才有效。
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^(.*)(\?.*?)(\#.*?)$" />
<action type="Redirect" url="{R:1}/{R:2}{R:3}" redirectType="Permanent" />
</rule>
注意: 我刚刚测试了最后一个 (^(.*)(\?.*?)(\#.*?)$
),它实际上不起作用。如果 url 在 ?
之前已经包含一个 /
,那么测试应该通过了,所以我在这里还有更多的工作要做。
问题
我可以使用一个正则表达式来解决这个问题,还是我需要使用多个规则?
您可以尝试使用此正则表达式 https://regex101.com/r/6TSqaP/2。如果 url 已经有结尾 '/'
.
,这将匹配每个提供的示例并解决问题
^((?:https?:\/\/[\w\.\-]*)(?:[\w\-]+\/)*(?:[\w\-]+)(?!\/))(\?.*?)?(\#.*?)?$
我使用您的第二个示例作为我的正则表达式的基础,具有以下逻辑。
url的部分:scheme://authority/path?query#fragment
- 第一个捕获组匹配 url
的 scheme://authority/path
部分
- 第二个捕获组可选并匹配
?query
- 第三个捕获组也是可选的,并且用于
#fragment
正则表达式解释
^( # should start with this
(?:https?:\/\/[\w\.\-]*) # match the http or https protocol and the domain
(?:[\w\-]+\/)* # match the path except the last element of it (optional)
(?:[\w\-]+)(?!\/) # match the last path element, but only if it's not closed with '/'
) # {R:1}
(\?.*?)? # {R:2} query (optional)
(\#.*?)? # {R:3} fragment (optional)
$ # string should end
Nginx
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^((?:https?:\/\/[\w\.\-]*)(?:[\w\-]+\/)*(?:[\w\-]+)(?!\/))(\?.*?)?(\#.*?)?$" />
<action type="Redirect" url="{R:1}/{R:2}{R:3}" redirectType="Permanent" />
</rule>
编辑:更新正则表达式以处理破折号 (-) 和多个路径元素
TL;DR
IIS 使用 Trailing Slash
重写(所有)URI 并保留 Fragment
and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)" />
<action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
so you can Try it here : https://regexr.com/6ele7
更新
IIS 使用 Trailing Slash
重写(考虑)URI 并保留 Fragment
and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$" />
<action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
在此处尝试:https://regexr.com/6fk3g
http://127.0.0.1 --> http://127.0.0.1/
https://localhost --> https://localhost/
https://localhost? --> https://localhost/?
https://localhost/ --> https://localhost/
https://my.site.com --> https://my.site.com/
https://my.site.com:443? --> https://my.site.com:443/?
https://my.site.com/ --> https://my.site.com/
https://my.site.com/about.php --> https://my.site.com/about.php
https://my.site.com/about.php? --> https://my.site.com/about.php?
https://my.site.com/about --> https://my.site.com/about/
https://my.site.com/about? --> https://my.site.com/about/?
https://my.site.com/about/ --> https://my.site.com/about/
https://my.site.com/about/? --> https://my.site.com/about/?
https://my.site.com/about?query --> https://my.site.com/about/?query
https://my.site.com/about/?query --> https://my.site.com/about/?query
https://my.site.com/about.php?query --> https://my.site.com/about.php?query
https://my.site.com/about#hash --> https://my.site.com/about/#hash
https://my.site.com/about/#hash --> https://my.site.com/about/#hash
https://my.site.com/about.php#hash --> https://my.site.com/about.php#hash
https://my.site.com/about?query#hash --> https://my.site.com/about/?query#hash
https://my.site.com/about/?query#hash --> https://my.site.com/about/?query#hash
https://my.site.com/folder.name/about?query --> https://my.site.com/folder.name/about/?query
https://my.site.com/about?query#hash:http://test.com?q --> https://my.site.com/about/?query#hash:http://test.com?q
说明(全部)
- 1 级 - 让我们想想你的例子:
^([^?#]+?)\/?([?#].*)?$
Group #1: ^
首先,[^?#]
除?
/#
以外的任意字符,走多了但是lazy +?
(首先停止,通过查看下一个)
忽略: \/?
那么如果/
存在与否
第 2 组: [?#]
= ?
/#
和 .*
旁边的任何字符,直到 $
结束, (...)?
如果存在
效果很好。 但是它不能正确处理:
https://my.site.com/about.php?query --> https://my.site.com/about.php/?query !!!
所以让我们添加一个例外...
- 级别 2 - 如果我们将可能的文件名
Name.name.name.ext
作为 Group #2 怎么办?
^([^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?)$
(?:...)
Non-Capturing组
([^/?#]+\.[^/?#]+)?
查找任何可能的文件名或 (?:[?#].*)?
任何可能的查询或锚字符串
现在一切正常,除了这个:
https://my.site.com? --> https://my.site.com? !!!
所以我们需要在 组 #1
中另一个例外
- 级别 3 - 仅使用域 URI 作为替代
^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)
(...|...)
备选方案
[^/]+:\/\/[^/#?]+
首先检查(不是懒惰)是否存在 ...://...
直到不存在 / # ?
之类的任何模式?
现在效果很好!
+ 说明(考虑)
- 4 级 - 如果我们只是在第一组中添加一个 Not-Accepting
.
& /
字符集来匹配考虑的 URI并忽略其他人?
^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$
\/[^/.?#]+
检查最后一个 /
字符集是否不是 /.?#
现在更小更快了!
正在分析其他方法
作为@károly-szabó answered well ,我们可以寻找匹配的模式,而不是寻找Not-Accepted个字符集。
因此,如果我们想使用该方法但以更简单的方式(2 组)(+ 一些小的优化),正则表达式将是:
^(https?:\/\/[\w.:-]+\/?(?:[\w.-]+\/)*[\w-]+(?!\/))([?#].*)?$
但是URI path Accepted characters更多。
因此,该正则表达式的更广泛版本可以是:
^(https?:\/\/[\w.:-]+\/?(?:[\w!#-)+-.;=@~]+\/)*[\w!#-);=@~+,-]+(?!\/))([?#].*)?$
在这里试试:https://regexr.com/6elea
注意:仍然是“multibyte Unicode 作为域名是允许的”,但我在此方法中忽略了这一点。
P.S.
实际上我认为我们不应该在 IIS 上重写它,原因如下:
- 锚点字符
#
可以是文件夹名称的一部分(by %23
)
- 文件名不能有扩展名
- IIS/Browsers 通常会(/应该)处理 Anchors/Queries
参考:
-
-
-
-
How does IIS URL Rewrite handle # anchor tags
我的意思是:
https://my.site.com/ --> (=Call root)
https://my.site.com/about --> (=Call root > Folder/File name about)
https://my.site.com/about/ --> (=Call root > Folder name about)
https://my.site.com/about?query --> (=Call root > Folder/File name about + Query)
https://my.site.com/about/?query --> (=Call root > Folder name about + Query)
https://my.site.com/about.php?query --> (=Call root > File name about.php + Query)
[When browser strip it:]
https://my.site.com/about#hash --> (=Call root > Folder/File name about + Anchor)
https://my.site.com/about/#hash --> (=Call root > Folder name about + Anchor)
https://my.site.com/about.php#hash --> (=Call root > File name about.php + Anchor)
[If not?]
https://my.site.com/folder#name/?query#hash
https://my.site.com/folder.name/about.php?query=one/two
我已经搜索了几个 SO post,但没有找到我要找的东西。它可能存在,但可能已经足够老了,不会出现在我面前。我发现 post (Nginx rewrite: add trailing slash, preserve anchors and query strings) 非常接近我的需要,但它的正则表达式解决方案不适用于 URL Rewrite for IIS,除非我做错了。
问题
我正在尝试在我的 url 路径末尾添加一个正斜杠 /
,同时还保留任何现有的查询字符串 ?
和锚点 #
.
期望的解决方案
基本上,这是每个问题的预期结果:
Entry: https://my.site.com/about
Result: https://my.site.com/about/
Entry: https://my.site.com/about?query=string
Result: https://my.site.com/about/?query=string
Entry: https://my.site.com/about#TestAnchor
Result: https://my.site.com/about/#TestAnchor
Entry: https://my.site.com/about?query=string#TestAnchor
Result: https://my.site.com/about/?query=string#TestAnchor
当前测试
我们当前的正则表达式忽略了查询字符串和锚点,但我现在想考虑它们。
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^.?]+[^.?/])$" />
<action type="Redirect" url="{R:1}/" redirectType="Permanent" />
</rule>
我还测试了另一个正则表达式,但它仅在 url 包含查询字符串和锚点时才有效。
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^(.*)(\?.*?)(\#.*?)$" />
<action type="Redirect" url="{R:1}/{R:2}{R:3}" redirectType="Permanent" />
</rule>
注意: 我刚刚测试了最后一个 (^(.*)(\?.*?)(\#.*?)$
),它实际上不起作用。如果 url 在 ?
之前已经包含一个 /
,那么测试应该通过了,所以我在这里还有更多的工作要做。
问题
我可以使用一个正则表达式来解决这个问题,还是我需要使用多个规则?
您可以尝试使用此正则表达式 https://regex101.com/r/6TSqaP/2。如果 url 已经有结尾 '/'
.
^((?:https?:\/\/[\w\.\-]*)(?:[\w\-]+\/)*(?:[\w\-]+)(?!\/))(\?.*?)?(\#.*?)?$
我使用您的第二个示例作为我的正则表达式的基础,具有以下逻辑。
url的部分:scheme://authority/path?query#fragment
- 第一个捕获组匹配 url 的
- 第二个捕获组可选并匹配
?query
- 第三个捕获组也是可选的,并且用于
#fragment
scheme://authority/path
部分
正则表达式解释
^( # should start with this
(?:https?:\/\/[\w\.\-]*) # match the http or https protocol and the domain
(?:[\w\-]+\/)* # match the path except the last element of it (optional)
(?:[\w\-]+)(?!\/) # match the last path element, but only if it's not closed with '/'
) # {R:1}
(\?.*?)? # {R:2} query (optional)
(\#.*?)? # {R:3} fragment (optional)
$ # string should end
Nginx
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^((?:https?:\/\/[\w\.\-]*)(?:[\w\-]+\/)*(?:[\w\-]+)(?!\/))(\?.*?)?(\#.*?)?$" />
<action type="Redirect" url="{R:1}/{R:2}{R:3}" redirectType="Permanent" />
</rule>
编辑:更新正则表达式以处理破折号 (-) 和多个路径元素
TL;DR
IIS 使用 Trailing Slash
重写(所有)URI 并保留 Fragment
and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)" />
<action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
更新
IIS 使用 Trailing Slash
重写(考虑)URI 并保留 Fragment
and Query Strings
<rule name="AddTrailingSlash" stopProcessing="true">
<match url="^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$" />
<action type="Redirect" url="{R:1}/{R:2}" redirectType="Permanent" />
</rule>
在此处尝试:https://regexr.com/6fk3g
http://127.0.0.1 --> http://127.0.0.1/
https://localhost --> https://localhost/
https://localhost? --> https://localhost/?
https://localhost/ --> https://localhost/
https://my.site.com --> https://my.site.com/
https://my.site.com:443? --> https://my.site.com:443/?
https://my.site.com/ --> https://my.site.com/
https://my.site.com/about.php --> https://my.site.com/about.php
https://my.site.com/about.php? --> https://my.site.com/about.php?
https://my.site.com/about --> https://my.site.com/about/
https://my.site.com/about? --> https://my.site.com/about/?
https://my.site.com/about/ --> https://my.site.com/about/
https://my.site.com/about/? --> https://my.site.com/about/?
https://my.site.com/about?query --> https://my.site.com/about/?query
https://my.site.com/about/?query --> https://my.site.com/about/?query
https://my.site.com/about.php?query --> https://my.site.com/about.php?query
https://my.site.com/about#hash --> https://my.site.com/about/#hash
https://my.site.com/about/#hash --> https://my.site.com/about/#hash
https://my.site.com/about.php#hash --> https://my.site.com/about.php#hash
https://my.site.com/about?query#hash --> https://my.site.com/about/?query#hash
https://my.site.com/about/?query#hash --> https://my.site.com/about/?query#hash
https://my.site.com/folder.name/about?query --> https://my.site.com/folder.name/about/?query
https://my.site.com/about?query#hash:http://test.com?q --> https://my.site.com/about/?query#hash:http://test.com?q
说明(全部)
- 1 级 - 让我们想想你的例子:
^([^?#]+?)\/?([?#].*)?$
Group #1: ^
首先,[^?#]
除?
/#
以外的任意字符,走多了但是lazy +?
(首先停止,通过查看下一个)
忽略: \/?
那么如果/
存在与否
第 2 组: [?#]
= ?
/#
和 .*
旁边的任何字符,直到 $
结束, (...)?
如果存在
效果很好。 但是它不能正确处理:
https://my.site.com/about.php?query --> https://my.site.com/about.php/?query !!!
所以让我们添加一个例外...
- 级别 2 - 如果我们将可能的文件名
Name.name.name.ext
作为 Group #2 怎么办?
^([^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?)$
(?:...)
Non-Capturing组
([^/?#]+\.[^/?#]+)?
查找任何可能的文件名或 (?:[?#].*)?
任何可能的查询或锚字符串
现在一切正常,除了这个:
https://my.site.com? --> https://my.site.com? !!!
所以我们需要在 组 #1
中另一个例外- 级别 3 - 仅使用域 URI 作为替代
^([^/]+:\/\/[^/#?]+|[^?#]+?)\/?((?:[^/?#]+\.[^/?#]+)?(?:[?#].*)?$)
(...|...)
备选方案
[^/]+:\/\/[^/#?]+
首先检查(不是懒惰)是否存在 ...://...
直到不存在 / # ?
之类的任何模式?
现在效果很好!
+ 说明(考虑)
- 4 级 - 如果我们只是在第一组中添加一个 Not-Accepting
.
&/
字符集来匹配考虑的 URI并忽略其他人?
^([^/]+:\/\/[^/#?]+|[^?#]+\/[^/.?#]+)([?#].*)?$
\/[^/.?#]+
检查最后一个 /
字符集是否不是 /.?#
现在更小更快了!
正在分析其他方法
作为@károly-szabó answered well
因此,如果我们想使用该方法但以更简单的方式(2 组)(+ 一些小的优化),正则表达式将是:
^(https?:\/\/[\w.:-]+\/?(?:[\w.-]+\/)*[\w-]+(?!\/))([?#].*)?$
但是URI path Accepted characters更多。
因此,该正则表达式的更广泛版本可以是:
^(https?:\/\/[\w.:-]+\/?(?:[\w!#-)+-.;=@~]+\/)*[\w!#-);=@~+,-]+(?!\/))([?#].*)?$
在这里试试:https://regexr.com/6elea
注意:仍然是“multibyte Unicode 作为域名是允许的”,但我在此方法中忽略了这一点。
P.S.
实际上我认为我们不应该在 IIS 上重写它,原因如下:
- 锚点字符
#
可以是文件夹名称的一部分(by%23
) - 文件名不能有扩展名
- IIS/Browsers 通常会(/应该)处理 Anchors/Queries
参考:How does IIS URL Rewrite handle # anchor tags
我的意思是:
https://my.site.com/ --> (=Call root)
https://my.site.com/about --> (=Call root > Folder/File name about)
https://my.site.com/about/ --> (=Call root > Folder name about)
https://my.site.com/about?query --> (=Call root > Folder/File name about + Query)
https://my.site.com/about/?query --> (=Call root > Folder name about + Query)
https://my.site.com/about.php?query --> (=Call root > File name about.php + Query)
[When browser strip it:]
https://my.site.com/about#hash --> (=Call root > Folder/File name about + Anchor)
https://my.site.com/about/#hash --> (=Call root > Folder name about + Anchor)
https://my.site.com/about.php#hash --> (=Call root > File name about.php + Anchor)
[If not?]
https://my.site.com/folder#name/?query#hash
https://my.site.com/folder.name/about.php?query=one/two