Async WebApi Thread.CurrentCulture

前端 未结 3 750
孤城傲影
孤城傲影 2020-12-01 01:31

I have a self-hosted OWIN hosted Web API project providing some basic REST methods for me.

I want to have multilingual error messag

相关标签:
3条回答
  • 2020-12-01 01:56

    Thread.CurrentCulture doesn't get synchronized across threads. However, your HttpContext does. You would be better off getting your culture information from your HttpContext directly. You can do something like

    public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
    {
        if (controllerContext.Request.Headers.AcceptLanguage != null && 
            controllerContext.Request.Headers.AcceptLanguage.Count > 0)
        {
            string language = controllerContext.Request.Headers.AcceptLanguage.First().Value;
            var culture = CultureInfo.CreateSpecificCulture(language);
            HttpContext.Current.Items["Culture"] = culture;
            //Thread.CurrentThread.CurrentCulture = culture;
            //Thread.CurrentThread.CurrentUICulture = culture;
        }
    
        base.ExecuteAsync(controllerContext, cancellationToken); 
    }
    

    and then, in any task you need the culture:

    var culture = HttpContext.Current != null ? HttpContext.Current.Items["Culture"] as CultureInfo : Thread.CurrentThread.CurrentCulture;
    
    0 讨论(0)
  • 2020-12-01 02:04

    As Joe pointed out, culture is transferred by the HttpContext in ASP.NET. The way ASP.NET does this is by installing a SynchronizationContext when a request starts, and that context is also used to resume asynchronous methods (by default).

    So, there are a couple of ways to approach the problem: you can either write your own SynchronizationContext that will preserve culture by default, or you can explicitly preserve the culture across each await.

    To preserve the culture at each await, you can use code from Stephen Toub:

    public static CultureAwaiter WithCulture(this Task task) 
    { 
        return new CultureAwaiter(task); 
    }
    
    public class CultureAwaiter : INotifyCompletion
    { 
        private readonly TaskAwaiter m_awaiter; 
        private CultureInfo m_culture;
    
        public CultureAwaiter(Task task) 
        { 
            if (task == null) throw new ArgumentNullException("task"); 
            m_awaiter = task.GetAwaiter(); 
        }
    
        public CultureAwaiter GetAwaiter() { return this; }
    
        public bool IsCompleted { get { return m_awaiter.IsCompleted; } }
    
        public void OnCompleted(Action continuation) 
        { 
            m_culture = Thread.CurrentThread.CurentCulture; 
            m_awaiter.OnCompleted(continuation); 
        }
    
        public void GetResult() 
        { 
            Thread.CurrentThread.CurrentCulture = m_culture; 
            m_awaiter.GetResult(); 
        } 
    }
    

    The SynchronizationContext approach is more complicated but once it's set up, it will be easier to use. I don't know of a good example of an ASP.NET-like context, but a good starting point is my MSDN article.

    0 讨论(0)
  • 2020-12-01 02:09

    From .NET 4.5, to set a default culture for all threads, use:

    CultureInfo.DefaultThreadCurrentCulture = culture;
    CultureInfo.DefaultThreadCurrentUICulture = culture;
    
    0 讨论(0)
提交回复
热议问题