我希望编写一个将URL转换为小写的HTTP模块.我的第一次尝试忽略了国际字符集,效果很好:
// Convert URL virtual path to lowercase
string lowercase = context.Request.FilePath.ToLowerInvariant();
// If anything changed then issue 301 Permanent Redirect
if (!lowercase.Equals(context.Request.FilePath, StringComparison.Ordinal))
{
context.Response.RedirectPermanent(...lowercase URL...);
}
Run Code Online (Sandbox Code Playgroud)
但是除了en-US之外的其他文化呢?我提到土耳其测试提出测试网址:
http://example.com/I??i
Run Code Online (Sandbox Code Playgroud)
这个小阴险的宝石破坏了URL中的大小写转换很简单的想法!它的小写和大写版本分别是:
http://example.com/??ii
http://example.com/II??
Run Code Online (Sandbox Code Playgroud)
要将大小写转换为使用土耳其语URL,我首先必须将ASP.NET的当前文化设置为土耳其语:
<system.web>
<globalization culture="tr-TR" />
</system.web>
Run Code Online (Sandbox Code Playgroud)
接下来,我不得不更改我的代码以使用当前文化进行大小写转换:
// Convert URL virtual path to lowercase
string lowercase = context.Request.FilePath.ToLower(CultureInfo.CurrentCulture);
// If anything changed then issue 301 Permanent Redirect
if (!lowercase.Equals(context.Request.FilePath, StringComparison.Ordinal))
{
context.Response.RedirectPermanent(...);
}
Run Code Online (Sandbox Code Playgroud)
可是等等!将StringComparison.Ordinal
仍然工作?或者我应该使用StringComparison.CurrentCulture
?我真的不确定!
即使以上工作,使用当前文化进行大小写转换会破坏 NTFS文件系统!假设我有一个名称为的静态文件I??i.html
:
http://example.com/I??i.html
Run Code Online (Sandbox Code Playgroud)
即使Windows文件系统不区分大小写,它也不使用语言文化.将上面的URL转换为小写会导致404 Not Found,因为文件系统不会将这两个名称视为相等:
http://example.com/??ii.html
Run Code Online (Sandbox Code Playgroud)
MSDN文章,在.NET Framework中使用字符串的最佳实践,有一个注释(大约在文章的一半):
注意: StringComparison.OrdinalIgnoreCase最能表示文件系统,注册表项和值以及环境变量的字符串行为.
咦?最好的代表??? 这是我们在C#中能做的最好的事情吗?那么正确的案例转换是什么以匹配文件系统?谁知道?!!?我们可以说的是,使用上面的字符串比较可能在大部分时间都有效.
StringComparison.OrdinalIgnoreCase
.请注意,没有string.ToLowerOrdinal()
方法,所以很难确切知道转换等同于OrdinalIgnoreCase
字符串比较的情况.使用string.ToLowerInvariant()
可能是最好的选择,但它打破了语言文化.string.ToLower(CultureInfo.CurrentCulture)
,但它会破坏文件系统匹配,有些不清楚存在可能违反此策略的边缘情况.因此,在选择两种转换方法之一之前,似乎案例转换首先需要检测URL是静态的还是动态的.对于静态URL,如何在不破坏Windows文件系统的情况下更改大小写是不确定的.对于动态URL,如果使用culture进行大小写转换会同样破坏URL,则会产生疑问.
呼!任何人都有这个烂摊子的解决方案?或者我应该闭上眼睛假装一切都是ASCII?
我会在这里挑战这个前提,即在尝试将URL自动转换为小写时有任何实用程序.
完整URL是否区分大小写完全取决于Web服务器,Web应用程序框架和基础文件系统.
您只能保证方案(http://等)和URL的主机名部分不区分大小写.请记住,不是所有的URL方案(file
和news
等),甚至还包括一个主机名.
其他一切可以区分大小写的服务器,包括路径(/
),文件名,查询(?
),片段(#
),和权威信息(用户名/前的密码@
中mailto
,http
,ftp
,和其他一些方案).
你们有一些不相容的目标。
\n\n具有文化敏感的大小写降低。如果土耳其语看起来很糟糕,您不想了解一些格鲁吉亚文字,不用介意它\xc3\x9f
是大写的SS
还是不太常见的SZ
- 在任何一种情况下都有一个完整的大小写折叠,其中lower("\xc3\x9f")
将匹配lower(upper("\xc3\x9f"))
您需要的认为它至少相当于这些两个字符序列之一。一般来说,如果可能的话,我们的目标是折叠大小写而不是降低大小写(这里不可能)。
在非文化敏感的环境中使用它。URI 最终是不透明的字符串。以便他们可以具有人类可读的理解,这对于编码人员、用户、搜索引擎和营销人员等都是有用的,但它们的最终工作是通过直接区分大小写的比较来识别资源。
将其映射到 NTFS,它具有基于 $UpCase 文件中的映射的区分大小写的功能,它通过比较单词的大写形式来实现这一点(至少它不必决定是否\xce\xa3
小写)案件到\xcf\x83
或\xcf\x82
,以文化不敏感的方式。
大概在搜索引擎优化和人类可读性方面做得很好。这很可能是您最初目标的一部分,但是虽然这不是很容易阅读或解析,但它对于人和机器来说都更容易。折叠案例会丢失信息。
我建议采用不同的方法。
\n\n从起始字符串开始,无论它是什么、来自何处(NTFS 文件名、数据库条目、web.config 中的 HttpHandler 绑定)。将其作为您的规范形式。无论如何,都有规则要求人们应该根据某种规范形式创建这些字符串,并可能在可能的情况下强制执行它,但是如果有什么事情违反了你的规则,那么无论多少,都接受它作为该资源的官方规范名称你不喜欢它。
规范名称应尽可能成为外界“看到”的唯一名称。这可以通过编程方式强制执行,或者只是最佳实践,因为事后使用 301 进行规范化并不能解决外部实体在取消引用 URI 之前不知道您这样做的问题。
收到请求后,根据它将如何使用进行测试。因此,虽然您可以选择在您自己使用所谓的“静态”URI 执行资源查找的情况下使用(或不使用)特定区域性,但您的逻辑可以故意遵循 NTFS 的逻辑,只需使用 NTFS 执行以下操作即可:工作:
\n\n编辑:
\n\n在某些方面,域名问题更为复杂。IDN 规则必须涵盖更多问题,而 man\xc5\x93uver 的空间却更少。然而,至少就案例规范化而言,它也更简单。
\n\n(我将忽略是否www.
使用的规范化等。虽然我猜这是同一工作的一部分,但它扩大了范围,我们最终可能会在如果我们不停下来的话,我们:)
IDN 在 RFC 3491 中定义了自己的大小写规范化(以及一些其他形式的规范化)规则。如果您要根据大小写规范化域名,请遵循该规则。
\n\n回答起来既美好又简单,不是吗?:)
\n\n在某种程度上压力也较小,因为虽然搜索引擎必须认识到这一点http://example.net/thisisapath
并且http://example.net/thisIsAPath
是相同的资源,但它们也必须认识到它们可能是不同的,这就是规范化的所有 SEO 优势所在其中之一(无论是哪一个)来自。
然而,他们知道这一点,example.net
并且EXAMPLE.NET
不可能是不同的网站,因此在确保它们相同方面几乎没有 SEO 优势(对于缓存和历史列表之类的东西来说仍然很好,这些东西不会跳跃他们自己)。当然,问题仍然在于,www.example.net
甚至maAndPasExampleEmporium.us
可能是同一个站点,但同样,这远离了案例问题。
还有一个简单的问题,大多数时候我们从来不需要处理超过几十个不同的域,所以有时更努力而不是更聪明地工作(即只需确保它们都设置正确并且不要\不以编程方式做任何事情!)可以做到这一点。
\n\n最后要注意的是,不要规范化第三方 URI,这一点很重要。如果你改变路径,你最终可能会破坏事情(他们可能不会不区分大小写地对待它),并且你至少可能最终会破坏他们稍微不同的规范化。最好始终保持原样。
\n