ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界
本系列文章從源碼分析的角度來探索 ASP.NET Core 的運(yùn)行原理,分為以下幾個(gè)章節(jié):
ASP.NET Core 運(yùn)行原理解剖[1]:Hosting
ASP.NET Core 運(yùn)行原理解剖[2]:Hosting補(bǔ)充之配置介紹
ASP.NET Core 運(yùn)行原理解剖[3]:Middleware-請(qǐng)求管道的構(gòu)成
IHttpContextFactory
在第一章中,我們介紹到,WebHost?在啟動(dòng)?IServer?時(shí),會(huì)傳入一個(gè)?IHttpApplication<TContext>?類型的對(duì)象,Server 負(fù)責(zé)對(duì)請(qǐng)求的監(jiān)聽,在接收到請(qǐng)求時(shí),會(huì)調(diào)用該對(duì)象的?ProcessRequestAsync?方法將請(qǐng)求轉(zhuǎn)交給我們的應(yīng)用程序。IHttpApplication<TContext>?的默認(rèn)實(shí)現(xiàn)為?HostingApplication?,有如下定義:
public class HostingApplication : IHttpApplication<HostingApplication.Context> { ?
?private readonly RequestDelegate _application; ?
?private readonly IHttpContextFactory _httpContextFactory; ?
public Context CreateContext(IFeatureCollection contextFeatures) ? ?{ ? ? ? ?var context = new Context(); ? ? ?
?var httpContext = _httpContextFactory.Create(contextFeatures); ? ? ? ?_diagnostics.BeginRequest(httpContext, ref context); ? ? ? ?context.HttpContext = httpContext; ? ?
? ?return context; ? ?} ?
?public Task ProcessRequestAsync(Context context) ? ?{ ? ?
? ?return _application(context.HttpContext); ? ?}
? ?public void DisposeContext(Context context, Exception exception) ? ?{ ? ? ? ?var httpContext = context.HttpContext; ? ? ? ?_diagnostics.RequestEnd(httpContext, exception, context); ? ? ? ?_httpContextFactory.Dispose(httpContext); ? ? ? ?_diagnostics.ContextDisposed(context); ? ?} }
首先使用?IHttpContextFactory?來創(chuàng)建?HttpContext?實(shí)例,然后在?ProcessRequestAsync?方法中調(diào)用上一章介紹的?RequestDelegate,由此進(jìn)入到我們的應(yīng)用程序當(dāng)中。
IHttpContextFactory 負(fù)責(zé)對(duì)?HttpContext?的創(chuàng)建和釋放,分別對(duì)應(yīng)著Create和Dispose方法,它的默認(rèn)實(shí)現(xiàn)類為HttpContextFactory,定義如下:
public class HttpContextFactory : IHttpContextFactory{ ??private readonly IHttpContextAccessor _httpContextAccessor; ?
??private readonly FormOptions _formOptions; ?
??
? ?public HttpContext Create(IFeatureCollection featureCollection) ?
? ?{ ? ? ?
?? ? ??var httpContext = new DefaultHttpContext(featureCollection); ? ? ? ? ? ?
?? ? ??if (_httpContextAccessor != null){_httpContextAccessor.HttpContext = httpContext;} ?
?? ??
?? ?? ?var formFeature = new FormFeature(httpContext.Request, _formOptions);featureCollection.Set<IFormFeature>(formFeature); ? ? ?
?? ?? ? ?return httpContext;} ? ?
?? ?
?? ?public void Dispose(HttpContext httpContext) ? ?{ ? ?
?? ? ? ?if (_httpContextAccessor != null){_httpContextAccessor.HttpContext = null;}} }
如上,HttpContextFactory 只是簡(jiǎn)單的使用?new DefaultHttpContext(featureCollection)?來創(chuàng)建 HttpContext 的實(shí)例,而這里涉及到一個(gè)?IFeatureCollection?對(duì)象,它是由 Server 根據(jù)原始請(qǐng)求創(chuàng)建而來的,下面就先介紹一下該對(duì)象。
IFeatureCollection
不過,在介紹?IFeatureCollection?之前,我們先需先回顧一下OWIN:
OWIN是 “Open Web Server Interface for .NET” 的首字母縮寫,它定義了一套Web Server和Web Application之間的標(biāo)準(zhǔn)接口,主要用于解除 ASP.NET 與 IIS 的緊密耦合。為此,OWIN 定義了四個(gè)核心組件:Host,?Server,?Middleware,?Application,并為Server和Middleware的之間的交互提供了一個(gè)?Func<IDictionary<string,object>,Task>?類型的標(biāo)準(zhǔn)接口。
每一個(gè)OWIN中間件,都會(huì)接收到一個(gè)?IDictionary<string,object>?類型的變量,用來表示當(dāng)前請(qǐng)求的相關(guān)信息,也稱為環(huán)境字典。每一個(gè)支持OWIN標(biāo)準(zhǔn)的 Web Server 都會(huì)根據(jù)請(qǐng)求的原始上下文信息,封裝成這個(gè)環(huán)境字典,然后在OWIN中間件之間傳遞,進(jìn)而完成整個(gè)請(qǐng)求的處理。環(huán)境字典定義了一系列預(yù)先約定好的Key,比如:用 "owin.RequestBody" 來表示請(qǐng)求體,"owin.RequestHeaders" 來表示請(qǐng)求頭,"owin.RequestMethod" 來表示請(qǐng)求方法等。
OWIN是隨著ASP.NET MVC5進(jìn)行到我們的視線中,在當(dāng)時(shí),ASP.NET WebAPI 2.0 也基于OWIN實(shí)現(xiàn)了自寄宿模式。再后來,提出了 ASP.NET 5 與 MVC6,完全是基于OWIN的模式來開發(fā)的,再到今天的 ASP.NET Core,OWIN的概念已被模糊化了,但是還是隨處可以見到OWIN的影子,并且也提供了對(duì) OWIN 的擴(kuò)展支持。
在 ASP.NET Core 中,提出了?IFeatureCollection?的概念,它本質(zhì)上也是一個(gè)?IDictionary<string,object>?鍵值對(duì),但是它具有面向?qū)ο蟮奶攸c(diǎn),相對(duì)于?IDictionary<string,object>?更加清晰,容易理解,并且Server構(gòu)建成這樣一個(gè)對(duì)象也很容易,它有如下定義:
public interface IFeatureCollection : IEnumerable<KeyValuePair<Type, object>> { ?? ? bool IsReadOnly { get; } ?
? ?int Revision { get; } ?
?? object this[Type key] { get; set; }TFeature Get<TFeature>(); ?
???void Set<TFeature>(TFeature instance); }
它的定義非常簡(jiǎn)單,由一系列以鍵值對(duì)來表示的標(biāo)準(zhǔn)特性對(duì)象(TFeature)組成,可以通過一個(gè)索引以及?Get?和?Set?方法來獲取或設(shè)置這些特性對(duì)象。
下面,我們看一下在 ASP.NET Core 中的對(duì)它的一個(gè)模擬實(shí)現(xiàn):
public class FeatureCollection : IFeatureCollection{ ??private IDictionary<Type, object> _features; ?
?
? ?private readonly IFeatureCollection _defaults; ?
? ?
? ? ?private volatile int _containerRevision; ?
? ? ?
? ? ??public virtual int Revision{ ? ? ?
? ? ?? ?get { return _containerRevision + (_defaults?.Revision ?? 0); }} ?
? ?
? ? ?public object this[Type key]{ ? ? ?
? ? ? ?get{ ? ? ? ? ?
? ? ? ??object result; ? ? ?
? ? ? ??? ? ?return _features != null && _features.TryGetValue(key, out result) ? result : _defaults?[key];} ? ?
? ? ? ?set{ ? ? ?
? ? ? ?? ? ?if (value == null){ ? ? ? ? ? ? ?
? ? ? ?? ? ? ?if (_features != null && _features.Remove(key)){_containerRevision++;} ? ? ? ? ?
? ? ? ?? ? ? ?? ? ?return;} ? ? ? ? ?
? ? ? ?? ?if (_features == null){_features = new Dictionary<Type, object>();}_features[key] = value;_containerRevision++;}} ?
? ? ? ?? ?
? ? ?public TFeature Get<TFeature>(){ ? ? ?
? ? ? ?return (TFeature)this[typeof(TFeature)];} ? ?
? ?
? ? public void Set<TFeature>(TFeature instance){ ? ? ? ?this[typeof(TFeature)] = instance;} ? ? }
如上,它的內(nèi)部屬性?_features?便是OWIN中的標(biāo)準(zhǔn)環(huán)境字典,并且提供了更加方便的泛型?Get,?Set?方法,以及一個(gè)索引器來訪問該環(huán)境字典。不過,如果只是這樣,那使用起來依然不夠方便,更為重要的是 ASP.NET Core 還提供了一系列的特性對(duì)象,并以這些特性對(duì)象的類型做為環(huán)境字典中的Key。
通過上面代碼,還可以發(fā)現(xiàn),每次對(duì)該環(huán)境字典的修改,都會(huì)使?Revision?屬性遞增1。
這里為什么說FeatureCollection是一個(gè)模擬的實(shí)現(xiàn)呢?具我觀察,FeatureCollection對(duì)象只在ASP.NET Core的測(cè)試代碼中用到,而每個(gè)Server都有它自己的方式來構(gòu)建IFeatureCollection,并不會(huì)使用FeatureCollection,關(guān)于Server中是如何創(chuàng)建IFeatureCollection實(shí)例的,可以參考KestrelHttpServer中的實(shí)現(xiàn),這里就不再深究。
那特性對(duì)象又是什么呢?我們先看一下請(qǐng)求特性的定義:
public interface IHttpRequestFeature{ ??string Protocol { get; set; } ?
??string Scheme { get; set; } ?
???string Method { get; set; } ? ?
???string PathBase { get; set; } ?
????string Path { get; set; } ?
???? ?string QueryString { get; set; } ?
???? ?
???? ??string RawTarget { get; set; }IHeaderDictionary Headers { get; set; }Stream Body { get; set; } }
再看一下表單特性的定義:
public interface IFormFeature{ ?? ? ?bool HasFormContentType { get; }IFormCollection Form { get; set; }
?? ?IFormCollection ReadForm(); ? ?Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken); }
可以看到,這些特性對(duì)象與我們熟悉的?HttpContext?中的屬性非常相似,這也就大大簡(jiǎn)化了在?IHttpRequestFeature?和?HttpContext?之間的轉(zhuǎn)換。我們可以通過這些特性接口定義的屬性來獲取到原始上下文中描述的信息,并通過特性對(duì)象提供的方法來操作原始上下文,它就像Web Server與我們的應(yīng)用程序之間的橋梁,完成抽象和具體之間的轉(zhuǎn)換。
ASP.NET Core 提供了一系列豐富的特性對(duì)象,如 Session, Cookies, Query, Form, WebSocket, Request, Response 等等, 更詳細(xì)的列表可以查看?Microsoft.AspNetCore.Http.Features。
HttpContext
HttpContext 對(duì)象我們應(yīng)該都很熟悉了,它用來表示一個(gè)抽象的HTTP上下文,而HttpContext對(duì)象的核心又體現(xiàn)在用于描述請(qǐng)求的Request和描述響應(yīng)的Response屬性上。除此之外,它還包含一些與當(dāng)前請(qǐng)求相關(guān)的其他上下文信息,如描述當(dāng)前HTTP連接的ConnectionInfo對(duì)象,控制WebSocket的WebSocketManager,代表當(dāng)前用戶的ClaimsPrincipal對(duì)象的Session,等等:
public abstract class HttpContext{ ??public abstract IFeatureCollection Features { get; } ?
? ? ? ?public abstract HttpRequest Request { get; } ?
?? ? ??public abstract HttpResponse Response { get; }
?? ?? ?public abstract ConnectionInfo Connection { get; }
?? ?? ?public abstract WebSocketManager WebSockets { get; } ?
?? ?? ?public abstract ClaimsPrincipal User { get; set; } ?
?? ?? ?public abstract IDictionary<object, object> Items { get; set; } ?
?? ?? ?public abstract IServiceProvider RequestServices { get; set; } ?
?? ?? ?public abstract CancellationToken RequestAborted { get; set; } ? ?
?? ?? ?public abstract string TraceIdentifier { get; set; } ?
?? ?? ?public abstract ISession Session { get; set; } ?
?? ?? ?public abstract void Abort(); }
在我們處理請(qǐng)求時(shí),如果希望終止該請(qǐng)求,可以通過?RequestAborted?屬性給請(qǐng)求管道發(fā)送一個(gè)終止信息。當(dāng)需要對(duì)整個(gè)管道共享一些與當(dāng)前上下文相關(guān)的數(shù)據(jù),可以將它保存在?Items?字典中。而在 ASP.NET Coer 1.x 中還包含一個(gè)管理認(rèn)證的AuthenticationManager對(duì)象,但是在 2.0 中,將它移到了?AuthenticationHttpContextExtensions?中,因?yàn)橛脩粽J(rèn)證本來就一個(gè)相對(duì)復(fù)雜且獨(dú)立的模塊,把它獨(dú)立出去會(huì)更加符合 ASP.NET Core 的簡(jiǎn)潔模塊化特性。
在上文中,我們了解到 HttpContext 的默認(rèn)實(shí)現(xiàn)使用的是?DefaultHttpContext?類型 ,而 DefaultHttpContext 便是對(duì)上面介紹的?IFeatureCollection?對(duì)象的封裝:
public class DefaultHttpContext : HttpContext{ ? ?private FeatureReferences<FeatureInterfaces> _features; ? ?private HttpRequest _request; ? ?private HttpResponse _response; ? ?public DefaultHttpContext(IFeatureCollection features) ? ?{Initialize(features);} ? ?public virtual void Initialize(IFeatureCollection features) ? ?{_features = new FeatureReferences<FeatureInterfaces>(features);_request = InitializeHttpRequest();_response = InitializeHttpResponse();} ? ?protected virtual HttpRequest InitializeHttpRequest() => new DefaultHttpRequest(this); }如上,DefaultHttpContext通過?Initialize?來完成從 IFeatureCollection 到 HttpContext 的轉(zhuǎn)換,而各個(gè)屬性的轉(zhuǎn)換又交給了它們自己。
HttpRequest
HttpRequest 可以用來獲取到描述當(dāng)前請(qǐng)求的各種相關(guān)信息,比如請(qǐng)求的協(xié)議(HTTP或者HTTPS)、HTTP方法、地址,以及該請(qǐng)求的請(qǐng)求頭,請(qǐng)求體等:
public abstract class HttpRequest{ ?? ?public abstract HttpContext HttpContext { get; }
?? ?public abstract string Method { get; set; } ?
?? ?public abstract string Scheme { get; set; } ?
?? ?public abstract bool IsHttps { get; set; } ?
?? ?public abstract HostString Host { get; set; }
?? ?public abstract PathString PathBase { get; set; } ?
?? ?public abstract PathString Path { get; set; }
?? ?public abstract QueryString QueryString { get; set; }
? ??public abstract IQueryCollection Query { get; set; } ?
? ??public abstract string Protocol { get; set; } ?
? ??public abstract IHeaderDictionary Headers { get; }
? ??public abstract IRequestCookieCollection Cookies { get; set; }
? ??public abstract long? ContentLength { get; set; } ?
? ??public abstract string ContentType { get; set; }
? ??public abstract Stream Body { get; set; }
? ??public abstract bool HasFormContentType { get; }
? ??public abstract IFormCollection Form { get; set; } ?
? ??public abstract Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken = new CancellationToken()); }
HttpRequest是一個(gè)抽象類,它的默認(rèn)實(shí)現(xiàn)是DefaultHttpRequest:
public class DefaultHttpRequest : HttpRequest{ ? ?private readonly static Func<IFeatureCollection, IHttpRequestFeature> _nullRequestFeature = f => null; ? ?
private FeatureReferences<FeatureInterfaces> _features; ? ?
?public DefaultHttpRequest(HttpContext context) ? ?{Initialize(context);} ?
?
??public virtual void Initialize(HttpContext context) ? ?{_context = context;_features = new FeatureReferences<FeatureInterfaces>(context.Features);} ?
??
???private IHttpRequestFeature HttpRequestFeature => _features.Fetch(ref _features.Cache.Request, _nullRequestFeature); ?
???
???public override string Method{ ? ? ?
????get { return HttpRequestFeature.Method; } ?
???? ? ? ?set { HttpRequestFeature.Method = value; }}
? ?public override Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken) ? ?{ ? ?
? ? ? ?return FormFeature.ReadFormAsync(cancellationToken);} }
在 DefaultHttpRequest 中,并沒有額外的功能,它只是簡(jiǎn)單的與?IHttpRequestFeature?中的同名屬性和方法做了一個(gè)映射,而 IHttpRequestFeature 對(duì)象的獲取又涉及到一個(gè)?FeatureReferences<FeatureInterfaces>?類型, 從字面意思來說,就是對(duì)Feature對(duì)象的一個(gè)引用,用來保存對(duì)應(yīng)的Feature實(shí)例,并在上文介紹的?Revision?屬性發(fā)生變化時(shí),清空Feature實(shí)例的緩存:
public struct FeatureReferences<TCache> { ? ?public IFeatureCollection Collection { get; private set; } ?
?public int Revision { get; private set; } ?
?
? ?public TCache Cache;[MethodImpl(MethodImplOptions.AggressiveInlining)] ? ?
? ?public TFeature Fetch<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory) where TFeature : class{ ? ? ?
? ? ? ?var flush = false; ? ?
? ?? ??var revision = Collection.Revision; ?
? ?? ??if (Revision != revision){cached = null;flush = true;} ? ? ?
? ?? ???return cached ?? UpdateCached(ref cached, state, factory, revision, flush);} ?
?
??private TFeature UpdateCached<TFeature, TState>(ref TFeature cached, TState state, Func<TState, TFeature> factory, int revision, bool flush) where TFeature : class{ ? ? ? ?if (flush){Cache = default(TCache);}cached = Collection.Get<TFeature>(); ? ?
??? ?if (cached == null){cached = factory(state);Collection.Set(cached);Revision = Collection.Revision;} ? ? ?
??? ??else if (flush) ? ?
? ? ?{Revision = revision;} ? ? ?
??? ???return cached;} ?
???
????public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> factory) ?
????? ?where TFeature : class => Fetch(ref cached, Collection, factory); }
如上,當(dāng)?Revision?生成變化時(shí),會(huì)將?Cache?設(shè)置為 null , 然后重新從?IFeatureCollection?中獲取,最后更新?Revision?為最新版本,相當(dāng)于一個(gè)緩存工廠。
Fetch方法使用了[MethodImpl(MethodImplOptions.AggressiveInlining)]特性,表示該方法會(huì)盡可能的使用內(nèi)聯(lián)方式來執(zhí)行。而內(nèi)聯(lián)是一種很重要的優(yōu)化方式, 它允許編譯器在方法調(diào)用開銷比方法本身更大的情況下消除對(duì)方法調(diào)用的開銷,即直接將該方法體嵌入到調(diào)用者中。
HttpResponse
在了解了表示請(qǐng)求的抽象類?HttpRequest?之后,我們?cè)賮碚J(rèn)識(shí)一下與它對(duì)應(yīng)的,用來描述響應(yīng)的?HttpResponse?類型:
public abstract class HttpResponse{ ??private static readonly Func<object, Task> _callbackDelegate = callback => ((Func<Task>)callback)(); ?
?
? ?private static readonly Func<object, Task> _disposeDelegate = disposable =>{((IDisposable)disposable).Dispose(); ? ? ?
? ? ?return Task.CompletedTask;}; ?
? ? ?
? ? ?public abstract HttpContext HttpContext { get; } ?
? ? ?
? ? ?public abstract int StatusCode { get; set; } ?
? ? ?
? ? ?public abstract IHeaderDictionary Headers { get; }
? ? ?
? ? ?public abstract Stream Body { get; set; } ?
? ? ?
? ? ?public abstract long? ContentLength { get; set; } ?
? ? ?
? ? ?public abstract string ContentType { get; set; } ?
? ? ?
? ? ?public abstract IResponseCookies Cookies { get; } ?
? ? ?
? ? ?public abstract bool HasStarted { get; } ?
? ? ?
? ? ?public abstract void OnStarting(Func<object, Task> callback, object state); ?
? ? ?
? ? ?public virtual void OnStarting(Func<Task> callback) => OnStarting(_callbackDelegate, callback); ?
? ? ?
? ? ?public abstract void OnCompleted(Func<object, Task> callback, object state); ?
? ? ?
? ? ?public virtual void RegisterForDispose(IDisposable disposable) => OnCompleted(_disposeDelegate, disposable); ?
? ? ?
? ? ?public virtual void OnCompleted(Func<Task> callback) => OnCompleted(_callbackDelegate, callback); ?
? ? ?
? ? ?public virtual void Redirect(string location) => Redirect(location, permanent: false); ?
? ? ?
? ? ?public abstract void Redirect(string location, bool permanent); }
HttpResponse也是一個(gè)抽象類,我們使用它來輸出對(duì)請(qǐng)求的響應(yīng),如設(shè)置HTTP狀態(tài)碼,Cookies,HTTP響應(yīng)報(bào)文頭,響應(yīng)主體等,以及提供了一些將響應(yīng)發(fā)送到客戶端時(shí)的相關(guān)事件。
其?HasStarted?屬性用來表示響應(yīng)是否已開始發(fā)往客戶端,在我們第一次調(diào)用?response.Body.WriteAsync?方法時(shí),該屬性便會(huì)被設(shè)置為?True。需要注意的是,一旦?HasStarted?設(shè)置為?true?后,便不能再修改響應(yīng)頭,否則將會(huì)拋出?InvalidOperationException?異常,也建議我們?cè)贖asStarted設(shè)置為true后,不要再對(duì) Response 進(jìn)行寫入,因?yàn)榇藭r(shí)?content-length?的值已經(jīng)確定,繼續(xù)寫入可能會(huì)造成協(xié)議沖突。
HttpResponse 的默認(rèn)實(shí)現(xiàn)為 DefaultHttpResponse ,它與 DefaultHttpRequest 類似,只是對(duì)?IHttpResponseFeature?的封裝,不過 ASP.NET Core 也為我們提供了一些擴(kuò)展方法,如:我們?cè)趯懭腠憫?yīng)時(shí),通常使用的是 Response 的擴(kuò)展方法?WriteAsync?:
public static class HttpResponseWritingExtensions{ ??public static Task WriteAsync(this HttpResponse response, string text, CancellationToken cancellationToken = default(CancellationToken)) ? ?{ ? ? ? ?return response.WriteAsync(text, Encoding.UTF8, cancellationToken);} ?
?
?public static Task WriteAsync(this HttpResponse response, string text, Encoding encoding, CancellationToken cancellationToken = default(CancellationToken)) ? ?{ ? ? ?
? ?byte[] data = encoding.GetBytes(text); ? ?
? ??return response.Body.WriteAsync(data, 0, data.Length, cancellationToken);} }
ASP.NET Core 還為 Response 提供了用來一個(gè)清空響應(yīng)頭和響應(yīng)體的擴(kuò)展方法:
public static class ResponseExtensions{ ? ?public static void Clear(this HttpResponse response) ? ?{ ?
? ? ?if (response.HasStarted){ ? ? ? ? ?
? ? ??throw new InvalidOperationException("The response cannot be cleared, it has already started sending.");}response.StatusCode = 200;response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = null;response.Headers.Clear(); ? ?
? ? ?? ?if (response.Body.CanSeek){response.Body.SetLength(0);}} }
還有比較常用的發(fā)送文件的擴(kuò)展方法:SendFileAsync?,獲取響應(yīng)頭的擴(kuò)展方法:GetTypedHeaders?等等,就不再細(xì)說。
IHttpContextAccessor
在 ASP.NET 4.x 我們經(jīng)常會(huì)通過?HttpContext.Current?來獲取當(dāng)前請(qǐng)求的?HttpContext?對(duì)象,而在 ASP.NET Core 中,HttpContext 不再有?Current?屬性,并且在 ASP.NET Core 中一切皆注入,更加推薦使用注入的方式來獲取實(shí)例,而非使用靜態(tài)變量。因此,ASP.NET Core 提供了一個(gè)?IHttpContextAccessor接口,用來統(tǒng)一獲取當(dāng)前請(qǐng)求的 HttpContext 實(shí)例的方式:
public interface IHttpContextAccessor{HttpContext HttpContext { get; set; } }它的定義非常簡(jiǎn)單,就只有一個(gè) HttpContext 屬性,它在ASP.NET Core 中還有一個(gè)內(nèi)置的實(shí)現(xiàn)類:HttpContextAccessor。
public class HttpContextAccessor : IHttpContextAccessor{ ? ?private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); ?
?public HttpContext HttpContext{ ? ? ?
?? ? ? get{ ? ? ? ?
??? ?return _httpContextCurrent.Value;} ? ? ?
??? ??set{_httpContextCurrent.Value = value;}} }
這里使用了一個(gè)?AsyncLocal<T>?類型來保存 HttpContext 對(duì)象,可能很多人對(duì)?AsyncLocal?不太了解,這里就來介紹一下:
在.NET 4.5 中引用了?async?await?等關(guān)鍵字,使我們可以像編寫同步方法一樣方便的來執(zhí)行異步操作,因此我們的大部分代碼都會(huì)使用異步。以往我們所使用的?ThreadLocal?在同步方法中沒有問題,但是在?await?后有可能會(huì)創(chuàng)建新實(shí)的例(await 之后可能還交給之前的線程執(zhí)行,也有可能是一個(gè)新的線程來執(zhí)行),而不再適合用來保存線程內(nèi)的唯一實(shí)例,因此在 .NET 4.6 中引用了?AsyncLocal<T>?類型,它類似于 ThreadLocal,但是在?await?之后就算切換線程也仍然可以保持同一實(shí)例。我們知道在 ASP.NET 4.x 中,HttpContext的?Current?實(shí)例是通過?CallContext?對(duì)象來保存的,但是 ASP.NET Core 中不再支持CallContext,故使用?AsyncLocal<T>?來保證線程內(nèi)的唯一實(shí)例。
不過,ASP.NET Core 默認(rèn)并沒有注入?IHttpContextAccessor?對(duì)象,如果我們想在應(yīng)用程序中使用它,則需要手動(dòng)來注冊(cè):
public void ConfigureServices(IServiceCollection services){ervices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }在上面介紹的HttpContextFactory類的構(gòu)造函數(shù)中會(huì)注入IHttpContextAccessor實(shí)例,并為其HttpContext屬性賦值,并在Dispose方法中將其設(shè)置為null。
總結(jié)
在ASP.NET 4.x 中,我們就對(duì) HttpContext 非常熟悉了,而在 ASP.NET Core 中,它的變化并不大,只是做了一些簡(jiǎn)化,因此本文較為簡(jiǎn)單,主要描述了一下 HttpContext 是如何創(chuàng)建的,以及它的構(gòu)成,最后則介紹了一下在每個(gè)請(qǐng)求中獲取 HttpContext 唯一實(shí)例的方式,而在 ASP.NET Core 2.0 中 HttpContext 的?AuthenticationManager?對(duì)象已標(biāo)記為過時(shí),添加了一些擴(kuò)展方法來實(shí)現(xiàn)AuthenticationManager中的功能,下一章就來介紹一下 ASP.NET Core 中的認(rèn)證系統(tǒng)。
相關(guān)文章:?
.NET Core 2.0 正式發(fā)布信息匯總
.NET Standard 2.0 特性介紹和使用指南
.NET Core 2.0 的dll實(shí)時(shí)更新、https、依賴包變更問題及解決
.NET Core 2.0 特性介紹和使用指南
Entity Framework Core 2.0 新特性
體驗(yàn) PHP under .NET Core
.NET Core 2.0使用NLog
升級(jí)項(xiàng)目到.NET Core 2.0,在Linux上安裝Docker,并成功部署
解決Visual Studio For Mac Restore失敗的問題
ASP.NET Core 2.0 特性介紹和使用指南
.Net Core下通過Proxy 模式 使用 WCF
.NET Core 2.0 開源Office組件 NPOI
ASP.NET Core Razor頁面 vs MVC
Razor Page–Asp.Net Core 2.0新功能 ?Razor Page介紹
MySql 使用 EF Core 2.0 CodeFirst、DbFirst、數(shù)據(jù)庫(kù)遷移(Migration)介紹及示例
.NET Core 2.0遷移技巧之web.config配置文件
asp.net core MVC 過濾器之ExceptionFilter過濾器(一)
ASP.NET Core 使用Cookie驗(yàn)證身份
ASP.NET Core MVC – Tag Helpers 介紹
ASP.NET Core MVC – Caching Tag Helpers
ASP.NET Core MVC – Form Tag Helpers
ASP.NET Core MVC – 自定義 Tag Helpers
ASP.NET Core MVC – Tag Helper 組件
ASP.NET Core 運(yùn)行原理解剖[1]:Hosting
ASP.NET Core 運(yùn)行原理解剖[2]:Hosting補(bǔ)充之配置介紹
ASP.NET Core 運(yùn)行原理解剖[3]:Middleware-請(qǐng)求管道的構(gòu)成
原文地址:http://www.cnblogs.com/RainingNight/p/httpcontext-in-asp-net-core.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NuGet.org服务管理变更,提升中国
- 下一篇: Visual Studio2017 远程