在ASP.NET Core中怎么使用HttpContext.Current


一、前言

  我們都知道,ASP.NET Core作為最新的框架,在MVC5和ASP.NET WebForm的基礎上做了大量的重構。如果我們想使用以前版本中的HttpContext.Current的話,目前是不可用的,因為ASP.NET Core中是並沒有這個API的。

  當然我們也可以通過在Controller中訪問HttpContext,但是某些情況下,這樣使用起來還是不如HttpContext.Current方便。

二、IHttpContextAccessor

  利用ASP.NET Core的依賴注入容器系統,通過請求獲取IHttpContextAccessor接口,我們擁有模擬使用HttpContext.Current這樣API的可能性。但是因為IHttpContextAccessor接口默認不是由依賴注入進行實例管理的。我們先要將它注冊到ServiceCollection中:

public void ConfigureServices(IServiceCollection services)
{
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}

  來模擬一個HttpContext.Current吧:

 public static class HttpContext
 {
        public static IServiceProvider ServiceProvider;

        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));
                Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
                return context;
            }
        }

}

  其實說到HttpContext.Current就不得不提到多線程問題,在以前的ASP.NET版本中,如果遇到多線程環境很有可能HttpContext.Current為空的情況。說到這個問題以前就是有解決方案的,那就是CallContext;

  CallContext 是類似於方法調用的線程本地存儲區的專用集合對象,並提供對每個邏輯執行線程都唯一的數據槽。數據槽不在其他邏輯線程上的調用上下文之間共享。當 CallContext 沿執行代碼路徑往返傳播並且由該路徑中的各個對象檢查時,可將對象添加到其中。

  當使用ASP.NET的時候,雖然線城池里的線程是復用的,但是CallContext並不在一個線程的多次使用中共享。因為CallContext是針對邏輯線程的TLS,線程池中被復用的線程是操作系統中的內核對象而不是托管對象。就像數據庫連接池中保存的是非托管資源而不是托管資源。因此,先后執行的兩個托管線程可能在底層復用了一個物理線程(內核對象),但並不能共享同一組CallContext數據槽。就像先后new的兩個SqlConnection對象可能在底層使用了同一個物理連接,但是托管對象的屬性已經被重置。

  與此對照的是ThreadStaticAttribute,標記上這個特性的靜態字段是往物理線程的TLS中保存數據(根據MSDN的描述猜的。具體沒試過),因此如果兩個托管線程對象內部使用的是同一個物理線程,則這個字段會復用(在兩個線程通過這一字段訪問同一個數據槽)。

  在.NET Core中,也有新的API選擇,AsyncLocal<T>。

三、HttpContextAccessor

  我們來看看ASP.NET Core中的IHttpContextAccessor接口實現吧:

 

 public class HttpContextAccessor : IHttpContextAccessor
 {
#if NET451
        private static readonly string LogicalDataKey = "__HttpContext_Current__" + AppDomain.CurrentDomain.Id;

        public HttpContext HttpContext
        {
            get
            {
                var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle;
                return handle?.Unwrap() as HttpContext;
            }
            set
            {
                CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value));
            }
        }

#elif NETSTANDARD1_3
        private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>();
        public HttpContext HttpContext
        {
            get
            {
                return _httpContextCurrent.Value;
            }
            set
            {
                _httpContextCurrent.Value = value;
            }
        }
#endif
}

 

  最后我只能說在ASP.NET Core中是萬物皆DI啊,其實Core中的實現早就為我們想好了這些功能,只是改變了使用方式。

 

GitHub:https://github.com/maxzhang1985/YOYOFx  如果覺還可以請Star下, 歡迎一起交流。

 

.NET Core 開源學習群: 214741894  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM