ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界
本系列文章從源碼分析的角度來探索 ASP.NET Core 的運行原理,分為以下幾個章節:
ASP.NET Core 運行原理解剖[1]:Hosting
ASP.NET Core 運行原理解剖[2]:Hosting補充之配置介紹
ASP.NET Core 運行原理解剖[3]:Middleware-請求管道的構成
IHttpContextFactory
在第一章中,我們介紹到,WebHost?在啟動?IServer?時,會傳入一個?IHttpApplication<TContext>?類型的對象,Server 負責對請求的監聽,在接收到請求時,會調用該對象的?ProcessRequestAsync?方法將請求轉交給我們的應用程序。IHttpApplication<TContext>?的默認實現為?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?來創建?HttpContext?實例,然后在?ProcessRequestAsync?方法中調用上一章介紹的?RequestDelegate,由此進入到我們的應用程序當中。
IHttpContextFactory 負責對?HttpContext?的創建和釋放,分別對應著Create和Dispose方法,它的默認實現類為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 只是簡單的使用?new DefaultHttpContext(featureCollection)?來創建 HttpContext 的實例,而這里涉及到一個?IFeatureCollection?對象,它是由 Server 根據原始請求創建而來的,下面就先介紹一下該對象。
IFeatureCollection
不過,在介紹?IFeatureCollection?之前,我們先需先回顧一下OWIN:
OWIN是 “Open Web Server Interface for .NET” 的首字母縮寫,它定義了一套Web Server和Web Application之間的標準接口,主要用于解除 ASP.NET 與 IIS 的緊密耦合。為此,OWIN 定義了四個核心組件:Host,?Server,?Middleware,?Application,并為Server和Middleware的之間的交互提供了一個?Func<IDictionary<string,object>,Task>?類型的標準接口。
每一個OWIN中間件,都會接收到一個?IDictionary<string,object>?類型的變量,用來表示當前請求的相關信息,也稱為環境字典。每一個支持OWIN標準的 Web Server 都會根據請求的原始上下文信息,封裝成這個環境字典,然后在OWIN中間件之間傳遞,進而完成整個請求的處理。環境字典定義了一系列預先約定好的Key,比如:用 "owin.RequestBody" 來表示請求體,"owin.RequestHeaders" 來表示請求頭,"owin.RequestMethod" 來表示請求方法等。
OWIN是隨著ASP.NET MVC5進行到我們的視線中,在當時,ASP.NET WebAPI 2.0 也基于OWIN實現了自寄宿模式。再后來,提出了 ASP.NET 5 與 MVC6,完全是基于OWIN的模式來開發的,再到今天的 ASP.NET Core,OWIN的概念已被模糊化了,但是還是隨處可以見到OWIN的影子,并且也提供了對 OWIN 的擴展支持。
在 ASP.NET Core 中,提出了?IFeatureCollection?的概念,它本質上也是一個?IDictionary<string,object>?鍵值對,但是它具有面向對象的特點,相對于?IDictionary<string,object>?更加清晰,容易理解,并且Server構建成這樣一個對象也很容易,它有如下定義:
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); }
它的定義非常簡單,由一系列以鍵值對來表示的標準特性對象(TFeature)組成,可以通過一個索引以及?Get?和?Set?方法來獲取或設置這些特性對象。
下面,我們看一下在 ASP.NET Core 中的對它的一個模擬實現:
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;} ? ? }
如上,它的內部屬性?_features?便是OWIN中的標準環境字典,并且提供了更加方便的泛型?Get,?Set?方法,以及一個索引器來訪問該環境字典。不過,如果只是這樣,那使用起來依然不夠方便,更為重要的是 ASP.NET Core 還提供了一系列的特性對象,并以這些特性對象的類型做為環境字典中的Key。
通過上面代碼,還可以發現,每次對該環境字典的修改,都會使?Revision?屬性遞增1。
這里為什么說FeatureCollection是一個模擬的實現呢?具我觀察,FeatureCollection對象只在ASP.NET Core的測試代碼中用到,而每個Server都有它自己的方式來構建IFeatureCollection,并不會使用FeatureCollection,關于Server中是如何創建IFeatureCollection實例的,可以參考KestrelHttpServer中的實現,這里就不再深究。
那特性對象又是什么呢?我們先看一下請求特性的定義:
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); }
可以看到,這些特性對象與我們熟悉的?HttpContext?中的屬性非常相似,這也就大大簡化了在?IHttpRequestFeature?和?HttpContext?之間的轉換。我們可以通過這些特性接口定義的屬性來獲取到原始上下文中描述的信息,并通過特性對象提供的方法來操作原始上下文,它就像Web Server與我們的應用程序之間的橋梁,完成抽象和具體之間的轉換。
ASP.NET Core 提供了一系列豐富的特性對象,如 Session, Cookies, Query, Form, WebSocket, Request, Response 等等, 更詳細的列表可以查看?Microsoft.AspNetCore.Http.Features。
HttpContext
HttpContext 對象我們應該都很熟悉了,它用來表示一個抽象的HTTP上下文,而HttpContext對象的核心又體現在用于描述請求的Request和描述響應的Response屬性上。除此之外,它還包含一些與當前請求相關的其他上下文信息,如描述當前HTTP連接的ConnectionInfo對象,控制WebSocket的WebSocketManager,代表當前用戶的ClaimsPrincipal對象的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(); }
在我們處理請求時,如果希望終止該請求,可以通過?RequestAborted?屬性給請求管道發送一個終止信息。當需要對整個管道共享一些與當前上下文相關的數據,可以將它保存在?Items?字典中。而在 ASP.NET Coer 1.x 中還包含一個管理認證的AuthenticationManager對象,但是在 2.0 中,將它移到了?AuthenticationHttpContextExtensions?中,因為用戶認證本來就一個相對復雜且獨立的模塊,把它獨立出去會更加符合 ASP.NET Core 的簡潔模塊化特性。
在上文中,我們了解到 HttpContext 的默認實現使用的是?DefaultHttpContext?類型 ,而 DefaultHttpContext 便是對上面介紹的?IFeatureCollection?對象的封裝:
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 的轉換,而各個屬性的轉換又交給了它們自己。
HttpRequest
HttpRequest 可以用來獲取到描述當前請求的各種相關信息,比如請求的協議(HTTP或者HTTPS)、HTTP方法、地址,以及該請求的請求頭,請求體等:
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是一個抽象類,它的默認實現是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 中,并沒有額外的功能,它只是簡單的與?IHttpRequestFeature?中的同名屬性和方法做了一個映射,而 IHttpRequestFeature 對象的獲取又涉及到一個?FeatureReferences<FeatureInterfaces>?類型, 從字面意思來說,就是對Feature對象的一個引用,用來保存對應的Feature實例,并在上文介紹的?Revision?屬性發生變化時,清空Feature實例的緩存:
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); }
如上,當?Revision?生成變化時,會將?Cache?設置為 null , 然后重新從?IFeatureCollection?中獲取,最后更新?Revision?為最新版本,相當于一個緩存工廠。
Fetch方法使用了[MethodImpl(MethodImplOptions.AggressiveInlining)]特性,表示該方法會盡可能的使用內聯方式來執行。而內聯是一種很重要的優化方式, 它允許編譯器在方法調用開銷比方法本身更大的情況下消除對方法調用的開銷,即直接將該方法體嵌入到調用者中。
HttpResponse
在了解了表示請求的抽象類?HttpRequest?之后,我們再來認識一下與它對應的,用來描述響應的?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也是一個抽象類,我們使用它來輸出對請求的響應,如設置HTTP狀態碼,Cookies,HTTP響應報文頭,響應主體等,以及提供了一些將響應發送到客戶端時的相關事件。
其?HasStarted?屬性用來表示響應是否已開始發往客戶端,在我們第一次調用?response.Body.WriteAsync?方法時,該屬性便會被設置為?True。需要注意的是,一旦?HasStarted?設置為?true?后,便不能再修改響應頭,否則將會拋出?InvalidOperationException?異常,也建議我們在HasStarted設置為true后,不要再對 Response 進行寫入,因為此時?content-length?的值已經確定,繼續寫入可能會造成協議沖突。
HttpResponse 的默認實現為 DefaultHttpResponse ,它與 DefaultHttpRequest 類似,只是對?IHttpResponseFeature?的封裝,不過 ASP.NET Core 也為我們提供了一些擴展方法,如:我們在寫入響應時,通常使用的是 Response 的擴展方法?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 提供了用來一個清空響應頭和響應體的擴展方法:
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);}} }
還有比較常用的發送文件的擴展方法:SendFileAsync?,獲取響應頭的擴展方法:GetTypedHeaders?等等,就不再細說。
IHttpContextAccessor
在 ASP.NET 4.x 我們經常會通過?HttpContext.Current?來獲取當前請求的?HttpContext?對象,而在 ASP.NET Core 中,HttpContext 不再有?Current?屬性,并且在 ASP.NET Core 中一切皆注入,更加推薦使用注入的方式來獲取實例,而非使用靜態變量。因此,ASP.NET Core 提供了一個?IHttpContextAccessor接口,用來統一獲取當前請求的 HttpContext 實例的方式:
public interface IHttpContextAccessor{HttpContext HttpContext { get; set; } }它的定義非常簡單,就只有一個 HttpContext 屬性,它在ASP.NET Core 中還有一個內置的實現類:HttpContextAccessor。
public class HttpContextAccessor : IHttpContextAccessor{ ? ?private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); ?
?public HttpContext HttpContext{ ? ? ?
?? ? ? get{ ? ? ? ?
??? ?return _httpContextCurrent.Value;} ? ? ?
??? ??set{_httpContextCurrent.Value = value;}} }
這里使用了一個?AsyncLocal<T>?類型來保存 HttpContext 對象,可能很多人對?AsyncLocal?不太了解,這里就來介紹一下:
在.NET 4.5 中引用了?async?await?等關鍵字,使我們可以像編寫同步方法一樣方便的來執行異步操作,因此我們的大部分代碼都會使用異步。以往我們所使用的?ThreadLocal?在同步方法中沒有問題,但是在?await?后有可能會創建新實的例(await 之后可能還交給之前的線程執行,也有可能是一個新的線程來執行),而不再適合用來保存線程內的唯一實例,因此在 .NET 4.6 中引用了?AsyncLocal<T>?類型,它類似于 ThreadLocal,但是在?await?之后就算切換線程也仍然可以保持同一實例。我們知道在 ASP.NET 4.x 中,HttpContext的?Current?實例是通過?CallContext?對象來保存的,但是 ASP.NET Core 中不再支持CallContext,故使用?AsyncLocal<T>?來保證線程內的唯一實例。
不過,ASP.NET Core 默認并沒有注入?IHttpContextAccessor?對象,如果我們想在應用程序中使用它,則需要手動來注冊:
public void ConfigureServices(IServiceCollection services){ervices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); }在上面介紹的HttpContextFactory類的構造函數中會注入IHttpContextAccessor實例,并為其HttpContext屬性賦值,并在Dispose方法中將其設置為null。
總結
在ASP.NET 4.x 中,我們就對 HttpContext 非常熟悉了,而在 ASP.NET Core 中,它的變化并不大,只是做了一些簡化,因此本文較為簡單,主要描述了一下 HttpContext 是如何創建的,以及它的構成,最后則介紹了一下在每個請求中獲取 HttpContext 唯一實例的方式,而在 ASP.NET Core 2.0 中 HttpContext 的?AuthenticationManager?對象已標記為過時,添加了一些擴展方法來實現AuthenticationManager中的功能,下一章就來介紹一下 ASP.NET Core 中的認證系統。
相關文章:?
.NET Core 2.0 正式發布信息匯總
.NET Standard 2.0 特性介紹和使用指南
.NET Core 2.0 的dll實時更新、https、依賴包變更問題及解決
.NET Core 2.0 特性介紹和使用指南
Entity Framework Core 2.0 新特性
體驗 PHP under .NET Core
.NET Core 2.0使用NLog
升級項目到.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、數據庫遷移(Migration)介紹及示例
.NET Core 2.0遷移技巧之web.config配置文件
asp.net core MVC 過濾器之ExceptionFilter過濾器(一)
ASP.NET Core 使用Cookie驗證身份
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 運行原理解剖[1]:Hosting
ASP.NET Core 運行原理解剖[2]:Hosting補充之配置介紹
ASP.NET Core 運行原理解剖[3]:Middleware-請求管道的構成
原文地址:http://www.cnblogs.com/RainingNight/p/httpcontext-in-asp-net-core.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NuGet.org服务管理变更,提升中国
- 下一篇: Ubuntu amp;amp; GitL