【发布时间】:2012-01-24 08:00:04
【问题描述】:
将 URL 规范化为小写
我希望编写一个将 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...);
}
土耳其测试(国际文化):
但是除了美国以外的文化呢?我参考了Turkey Test 想出了一个测试网址:
http://example.com/Iıİi
这个阴险的小宝石破坏了 URL 中大小写转换很简单的任何观念!它的小写和大写版本分别是:
http://example.com/ııii
http://example.com/IIİİ
为了使用土耳其语 URL 进行大小写转换,我首先必须将 ASP.NET 的当前文化设置为土耳其语:
<system.web>
<globalization culture="tr-TR" />
</system.web>
接下来,我必须更改我的代码以使用当前文化进行大小写转换:
// 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(...);
}
但是等等! StringComparison.Ordinal 还能用吗?还是我应该使用StringComparison.CurrentCulture?我真的不确定!
文件名:变得更糟了!
即使上述方法有效,使用当前区域性进行大小写转换会破坏 NTFS 文件系统!假设我有一个名为 Iıİi.html 的静态文件:
http://example.com/Iıİi.html
即使 Windows 文件系统不区分大小写,它也不使用语言文化。将上述 URL 转换为小写会导致 404 Not Found,因为文件系统不认为这两个名称相等:
http://example.com/ııii.html
文件名的正确大小写转换?谁知道?!
MSDN 文章Best Practices for Using Strings in the .NET Framework 有注释(大约在文章中途):
注意: 文件系统、注册表项和值以及环境变量的字符串行为最好用 StringComparison.OrdinalIgnoreCase 表示。
嗯? 最好的表现???这是我们在 C# 中能做到的最好的吗?那么,匹配文件系统的正确大小写转换是什么? 谁知道?!!?我们只能说,使用上面的字符串比较可能在大多数情况下都有效。
总结:两种情况转换:静态/动态网址
- 所以我们已经看到 静态 URLs---文件路径与文件系统中的真实目录/文件匹配的 URL---必须使用仅“最好的代表“由
StringComparison.OrdinalIgnoreCase。请注意,没有string.ToLowerOrdinal()方法,因此很难确切知道什么大小写转换等同于OrdinalIgnoreCase字符串比较。使用string.ToLowerInvariant()可能是最好的选择,但它破坏了语言文化。 - 另一方面,动态网址---文件路径与磁盘上的真实文件(映射到您的应用程序)不匹配的网址---可以使用
string.ToLower(CultureInfo.CurrentCulture),但它会破坏文件系统匹配,而且还不清楚存在哪些边缘情况可能会破坏此策略。
因此,大小写转换似乎首先需要检测 URL 是静态还是动态,然后再选择两种转换方法之一。对于静态 URL,不确定如何在不破坏 Windows 文件系统的情况下更改大小写。对于动态 URL,如果使用区域性进行大小写转换同样会破坏 URL,则值得怀疑。
哇!有人有解决这个烂摊子的办法吗?还是我应该闭上眼睛假装一切都是ASCII?
【问题讨论】:
-
据我了解,小写/大写转换的安全案例可能只是基本的拉丁字母。 Unicode 包括许多语言,有些甚至可能没有大写字母之类的东西。这样,您可能只能对 127 以上的任何代码点使用完全匹配。
-
@John 转换基本拉丁语将是一个保守的解决方案,但 .NET 没有提供仅影响这些字符的
.ToLowerASCII()。.ToLowerInvariant()走得更远,摧毁了许多国际角色。我确定目前没有 100% 的解决方案。 -
@KevinR 考虑尝试手动枚举文件并使用
Accept-Language中的值?String.ToLower当您使用它进行比较时,它也是一个令人讨厌的蠕虫包。让我弄清楚如何进行不区分大小写的文化比较。 -
@JonathanDickinson 我没有使用
Accept-Language。CurrentCulture仅指服务器端应用程序的配置(除非您制作该轨道 Accept-Language)。人们会期望将具有土耳其语 URL 的服务器设置为该文化,尽管我认为在一台机器上可能存在多个文化 URL 的噩梦场景。也许 John 只转换 ASCII 字符是对的。 -
@KevinR 我会避免每个 URL 文化都具有文化意识。即使它是用英语编写的,用户可能仍然是土耳其人,并且会应用他们自己的大小写规则。在这种情况下,当前用户胜过创建者。