【问题标题】:ASP.Net Validating Culture NamesASP.Net 验证文化名称
【发布时间】:2009-01-26 16:36:02
【问题描述】:

我们有一个使用 asp.net 2.0 文化的多语言网站。当前文化是通过重写为查询字符串的 url 设置的。 (~/es/blah.aspx 是 ~/blah.aspx 的西班牙语版本 - 重写为 ~/blah.aspx?lang=es)

测试文化的代码如下:

    System.Globalization.CultureInfo ci;
    try
    {
        ci = new System.Globalization.CultureInfo(Request.QueryString["lang"] ?? string.Empty);
    }
    catch
    {
        ci = new System.Globalization.CultureInfo(string.Empty);
    }

如果没有设置文化,则默认为英语,127。当有文化时,该页面上的所有链接都会在前面加上正确的文化名称。

蜘蛛不知何故获得了 ~/www.test.com/blah.aspx 形式的一些链接,并以 www.test.com 的文化重创我们的网站,并淹没了我们的错误记录。

除了捕获异常之外,还有什么方法可以测试文化名称是否有效?

【问题讨论】:

    标签: c# .net asp.net culture


    【解决方案1】:

    我想我可以快速测量一下,所以我开发了一个快速控制台应用程序。

    它基本上使用所有 3 种方法(构造函数、LINQ 和 foreach)在循环中从字符串中获取 CultureInfo 10000 次。为简洁起见,我删除了秒表和控制台输出。

    string culture = "en-GB";
    CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
    for (int i = 0; i < 10000; i++)
    {
        try
        {
            CultureInfo c = new CultureInfo(culture);
        }
        catch
        {
        }
    }
    for (int i = 0; i < 10000; i++)
    {
        CultureInfo c = cultures.FirstOrDefault((x) => x.Name == culture);
    }
    for (int i = 0; i < 10000; i++)
    {
        foreach (CultureInfo c in cultures)
        {
            if (c.Name == culture)
                break;
        }
    }
    

    结果如下……

    Try Catch: 00:00:00.0023860
    LINQ: 00:00:00.0542459
    ForEach: 00:00:00.0238937
    

    如果您删除文化变量并在每次迭代中调用它,则 LINQ 和 ForEach 循环大约需要 2.5 秒。

    因此,如果您希望获得大量有效输入并且只获得奇数无效输入,则使用构造函数是有利的。但是,如果您将输入值从en-GB 更改为TEST,那么情况就会发生巨大变化。

    Invalid Culture Try Catch: 00:00:39.7163513
    Invalid Culture LINQ: 00:00:00.0791752
    Invalid Culture ForEach: 00:00:00.0291480
    

    显然,我的测试应用程序不是真实世界的场景,但由于 OP 说这是基于每个请求调用的,我可以想象在大型 Web 应用程序中,此代码可能会被调用很多次。这可能是一个拒绝或服务向量,通过向 Web 服务器发送垃圾邮件,这些请求都具有无效的文化参数,从而占用了所有 CPU。

    【讨论】:

      【解决方案2】:

      你没有使用 LINQ:

      private static bool IsValidCultureName(string cultureName)
      {
          CultureInfo[] cultures =
              CultureInfo.GetCultures(CultureTypes.SpecificCultures);
          foreach (CultureInfo culture in cultures)
          {
              if (culture.Name == cultureName)
                  return true;
          }
      
          return false;
      }
      

      这可能非常昂贵,但可能仍然比处理非常昂贵的异常便宜 - 不过,在相信我之前衡量一下差异。

      我认为定义的文化列表不会在 .NET 版本之间发生变化,因此您可能应该在启动时获取该列表并将其缓存在某处。

      【讨论】:

      • 我做了一些快速测试,看看哪种方法最好,我认为结果需要一个新的答案。如果您仍然感兴趣,请参见下文。
      • 在微软的代码中,他们已经在字典中缓存了文化列表,因此自己再次缓存没有太大意义。话虽如此,我们确实看到了通过缓存 CultureInfo 对象而不是在每个请求上构建一个新对象来获得可衡量的性能提升。
      【解决方案3】:

      与已经说明的答案几乎相同,只是使用更紧凑的 linq 表达式:

      private static bool IsValidCultureInfoName(string name)
      {
          return 
              CultureInfo
              .GetCultures(CultureTypes.SpecificCultures)
              .Any(c => c.Name == name);
      }
      

      【讨论】:

        【解决方案4】:

        This site 有一个使用 LINQ 的示例:

        CultureInfo[] cultures = System.Globalization.CultureInfo.GetCultures
                                 (CultureTypes.SpecificCultures);
        
        var selectCulture = from p in cultures
                            where p.Name == value
                            select p;
        
        if (selectCulture.Count() == 1)
        {
            // your culture is good
        }
        

        不过好像有点重。我可能会坚持你所拥有的。

        【讨论】:

        • 谢谢,但我应该提到我们仍在使用 .net 2.0
        • 您可以拉出您需要的零件。 Roger 为你把它变成了 .net 2.0。
        【解决方案5】:

        这就是我最终为我的动作过滤器所做的事情。 我们只检查语言,而不是区域设置。

        public class HttpInternationalizationAttribute : ActionFilterAttribute
        {
            private static readonly HashSet<string> Langs = new HashSet<string>(CultureInfo.GetCultures(CultureTypes.NeutralCultures)
                                                                                   .Select(x => x.TwoLetterISOLanguageName.ToUpper()));
        
            public override void OnActionExecuting(HttpActionContext actionContext)
            {
                var language = (string)actionContext.ControllerContext.RouteData.Values["language"];
        
                if (!string.IsNullOrEmpty(language) && Langs.Contains(language.ToUpper()))
                {
                    Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(language);
                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(language);
                }
            }
        }
        

        使用哈希集,这将在 O(1) 中运行。

        干杯!

        【讨论】:

        • 如果您使用 dictionary&lt;string, cultureInfo&gt; 来避免 GetCultureInfo 调用,它将显着加快速度。
        猜你喜欢
        • 1970-01-01
        • 2012-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多