Создание настраиваемого RequestCultureProvider - почему не инициализировано свойство Options? - PullRequest
0 голосов
/ 19 июня 2020

Я реализую ASP. NET Core 3.1 WebApp с использованием Razor Pages , который должен поддерживать несколько культур. Он будет работать на Azure. Он также должен поддерживать Asyn c методы .

Я создаю пользовательский RequestCultureProvider, чтобы я мог получить культуру из требований пользователяPrincipal, если вошел в систему, как предлагается Маттео . Если пользователь не заморожен, я хочу получить культуру из одного из значений по умолчанию RequestCultureProviders:

  1. Строка запроса в URL
  2. Cook ie .AspNetCore.Culture
  3. Настройка браузера Accept-Language

После получения культуры пользователя из любого источника я хочу сохранить ее в ". AspNetCore.Culture" cook ie . Таким образом, культура пользователя может быть получена на моих страницах Razor, используя

var culture = page.Request.Cookies[".AspNetCore.Culture"];

  • Примечание. Установка значения Cook ответа ie приводит к тому, что такое же значение становится доступным в запросе cook ie, как описано Microsoft

Это кажется лучшим подходом, чем попытка получить культуру из текущего потока, потому что, похоже, возникает множество проблем, когда делая это с помощью методов asyn c, как описано в GitHub и SO - Keep CurrentCulture в async / await .

По сути, эти проблемы возникают из-за того, что начальный поток, используемый Default RequestCultureProviders для установки культуры, может быть не тем же потоком, который использовался для запуска моих методов asyn c, и поскольку нет гарантии, что эти разные потоки будут иметь ту же культуру, что и исходный поток, я не могу быть уверен в получение культуры пользователя на моих страницах, прочитав:

var culture = Thread.CurrentThread.CurrentCulture.

Поэтому я решил написать a настраиваемый RequestCultureProvider , который пытается получить язык и региональные параметры либо из ClaimsPrincipal пользователя, либо из RequestCultureProviders по умолчанию, а затем устанавливает его в Cook культуре ie перед возвратом требуемого ProviderCultureResult. Я реализовал это следующим образом:

public class MyRequestCultureProvider : RequestCultureProvider
{
   public override async Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext)
   {
      string culture = "en";

      if (httpContext == null)
         throw new ArgumentNullException();

      if (httpContext.User.Identity.IsAuthenticated)
         culture = httpContext.User.GetCulture() ?? "en";
      else
      {
         var count = Options?.RequestCultureProviders?.Count ?? -1;
         for (var x = 1; x < count; x++)
         {
            var provider = Options.RequestCultureProviders[x]; //don't invoke initial provider as that's our one
            var val = await provider.DetermineProviderCultureResult(httpContext);
            if (val?.Cultures?.Count > 0)
            { 
               culture = val.Cultures[0].ToString() ?? "en";
               break;
            }
         }
      }
      if (httpContext.Request.Cookies[".AspNetCore.Culture"] != culture)
         httpContext.Response.Cookies.Append(".AspNetCore.Culture", culture, new CookieOptions { Expires = DateTime.Now.AddDays(3), IsEssential = true });
      return new ProviderCultureResult(culture, culture );
   }
}

Пользовательский RequestCultureProvider добавляется в промежуточное ПО в Startup следующим образом:

var supportedCultures = new[]
{
    new CultureInfo("en"),
    new CultureInfo("en-US"),
    new CultureInfo("de-CH"),
};

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   ...
   app.UseRequestLocalization();

   app.UseHttpsRedirection();
   app.UseStaticFiles();
   ...
}

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<RequestLocalizationOptions>(options =>
   {
      options.DefaultRequestCulture = new RequestCulture(culture: supportedCultures[0].Name, uiCulture: supportedCultures[0].Name);
      options.SupportedCultures = supportedCultures;
      options.SupportedUICultures = supportedCultures;
      options.AddInitialRequestCultureProvider(new MyRequestCultureProvider());
   });
   services.AddRazorPages();
}

ВОПРОСЫ:

  1. Почему Options null , когда я обращаюсь к нему в моем переопределенном методе DetermineProviderCultureResult ()? Это свойство базового класса, задокументированное Microsoft , так как его инициализировать?
  2. Как еще я могу получить значения культуры, установленные по умолчанию RequestCultureProviders? Вы заметите, что мой метод DetermineProviderCultureResult () является asyn c, поэтому простое чтение значения культуры, установленного в потоке, не кажется хорошей идеей.
  3. Как еще я могу получить культуру пользователя для использования в my asyn c Методы страницы?

Любые комментарии к моему коду также приветствуются.

1 Ответ

0 голосов
/ 20 июня 2020

Мне просто нужно было создать ctor для MyRequestCultureProvider с аргументом RequestLocalizationOptions, чтобы я мог установить параметры следующим образом:

public MyRequestCultureProvider(RequestLocalizationOptions options)
{
   Options = options;
}

Код в Startup.ConfigureServices затем необходимо изменить, чтобы передать объект Options, когда добавив его в коллекцию RequestCultureProviders, например:

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<RequestLocalizationOptions>(options =>
   {
      options.DefaultRequestCulture = new RequestCulture(culture: supportedCultures[0].Name, uiCulture: supportedCultures[0].Name);
      options.SupportedCultures = supportedCultures;
      options.SupportedUICultures = supportedCultures;
      options.AddInitialRequestCultureProvider(new MyRequestCultureProvider(options));
   });
   services.AddRazorPages();
}

Мне все еще интересно услышать комментарии об этом подходе к получению пользовательской культуры для ASP. NET Core WebApp с asyn c Razor методы страницы

...