ASP.Net проверяет названия культур - PullRequest
5 голосов
/ 26 января 2009

У нас есть многоязычный сайт, в котором используется культура 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, что и наводит нашу ошибку каротаж.

Есть ли способ проверить, допустимо ли имя культуры, кроме перехвата исключения?

Ответы [ 5 ]

10 голосов
/ 11 сентября 2012

Я подумал, что мне нужно быстро это измерить, поэтому запустил быстрое консольное приложение.

Он в основном использует все 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 сказал, что он вызывается для каждого запроса, я могу себе представить, что в большом веб-приложении этот код может вызываться много. Возможно, это вектор отказа или обслуживания, занимающий весь ЦП путем рассылки веб-серверу спама запросами, которые имеют недопустимый параметр культуры.

6 голосов
/ 07 октября 2011

Почти такой же ответ, как уже говорилось, только с более компактным выражением linq:

private static bool IsValidCultureInfoName(string name)
{
    return 
        CultureInfo
        .GetCultures(CultureTypes.SpecificCultures)
        .Any(c => c.Name == name);
}
5 голосов
/ 26 января 2009

У вас нет для использования 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, поэтому вы, вероятно, должны получить список при запуске и где-то его кешировать.

2 голосов
/ 26 января 2009

Этот сайт имеет пример использования 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
}

Хотя это кажется немного тяжелым. Я бы, наверное, придерживался того, что у тебя есть.

1 голос
/ 06 ноября 2012

Вот что я в итоге сделал для своего фильтра действий. Мы проверяем только язык, а не язык.

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).

Ура!

...