前言 最近這段時間除了工作,所有的時間都是在移植我以前實現的一個Owin框架,相當移植到到Core的話肯定會有很多坑,這個大家都懂,以後幾篇文章可能會圍繞這個說下,暫時就叫《Dotnet Core踩坑記》吧,呵呵。 接下來我對我在移植過程中發現的一些問題進行了總結,今天主要說說Owin。說到Owin ...
前言
最近這段時間除了工作,所有的時間都是在移植我以前實現的一個Owin框架,相當移植到到Core的話肯定會有很多坑,這個大家都懂,以後幾篇文章可能會圍繞這個說下,暫時就叫《Dotnet Core踩坑記》吧,呵呵。
接下來我對我在移植過程中發現的一些問題進行了總結,今天主要說說Owin。說到Owin就不能不提Katana項目和宇內大神的Tinyfox了,當然關於這兩塊內容這篇文章就不多涉及了,博友可以自己在博客園內搜索關於Owin的文章還是挺多的。
Owin
ASP.NET vNext剛推出的時候,號稱是Owin的一個實現,在 http://owin.org 上,直到現在還保留著這樣一段描述。
Implementations
- Katana
- Freya
- ASP.NET vNext
很多開發者紛紛實現著自己的Owin框架,也寫很多應用到了實際的生產環境中,當然我也是其中一員。
ASP.NET Core
移植過程中,會發現有很多的不同,還有遇到新的API不知道怎麼使用,這時候看文檔還不如直接看源碼來的痛快。
在看完AspCore.Mvc後才發現,一點關於Owin的內容都沒有;但很明顯官方文檔上說是支持Owin協議的,後來我硬著頭皮去看了看KestrelHttpServer和HttpAbstractions兩個項目,後來才發現原來真的沒有一點點的Owin協議內容啊(一定要給MS差評)。
好吧,只能看看MS是怎麼支持Owin的,在HttpAbstractions項目里發現了Microsoft.AspNetCore.Owin這樣一個子項目,看完只是想說:“你這意思這叫向下相容?” ,算了。 現在只要在Asp.net core項目裡加入依賴Microsoft.AspNet.Owin就可以IApplicationBuilder介面的擴展方法UseOwin進行Owin中間件的調用。如下:
添加依賴:
"dependencies": {
"Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
"Microsoft.AspNet.Owin": "1.0.0-*"
},
在Startup中加入UseOwin:
1 public void Configure(IApplicationBuilder app)
2 {
3 app.UseOwin(pipeline =>
4 {
5 pipeline(next => OwinHello);
6 });
7 }
當然OwinHello的內容一定是一個標準Owin中間件的內容了:
1 public Task OwinHello(IDictionary<string, object> environment)
2 {
3 string responseText = "Hello World via OWIN";
4 byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);
5 var responseStream = (Stream)environment["owin.ResponseBody"];
6 var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
7 responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
8 responseHeaders["Content-Type"] = new string[] { "text/plain" };
9 return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
10 }
Kestrel
既然新的伺服器已經不在支持Owin協議了,那個是怎麼通信的?
這個問題在ASP.NET Core管道深度剖析系列文章中被提到過一些,其實每一個HttpContext在被創建出來都會依賴一個IFeatureCollection集合。
IFeatureCollection這個介面用於描述某個對象所具有的一組特征,由一組Feature介面組成。
列出其中一個IHttpConnectionFeature介面,用於獲得Http的連接信息:
public class HttpConnectionFeature : IHttpConnectionFeature
{
public string ConnectionId { get; set; }
public IPAddress LocalIpAddress { get; set; }
public int LocalPort { get; set; }
public IPAddress RemoteIpAddress { get; set; }
public int RemotePort { get; set; }
}
閱讀kestrel源碼,發現每一次接受tcp連接,都會將Http流,封裝在一個幀Frame,它被描述成一個單向或雙向的request和response。 並組裝成特征集合供上層應用進行使用。
最後
最後就發一段Owin字典對應Feature的源碼吧:
_entries = new Dictionary<string, FeatureMap>()
{
{ OwinConstants.RequestProtocol, new FeatureMap<IHttpRequestFeature>(feature => feature.Protocol, () => string.Empty, (feature, value) => feature.Protocol = Convert.ToString(value)) },
{ OwinConstants.RequestScheme, new FeatureMap<IHttpRequestFeature>(feature => feature.Scheme, () => string.Empty, (feature, value) => feature.Scheme = Convert.ToString(value)) },
{ OwinConstants.RequestMethod, new FeatureMap<IHttpRequestFeature>(feature => feature.Method, () => string.Empty, (feature, value) => feature.Method = Convert.ToString(value)) },
{ OwinConstants.RequestPathBase, new FeatureMap<IHttpRequestFeature>(feature => feature.PathBase, () => string.Empty, (feature, value) => feature.PathBase = Convert.ToString(value)) },
{ OwinConstants.RequestPath, new FeatureMap<IHttpRequestFeature>(feature => feature.Path, () => string.Empty, (feature, value) => feature.Path = Convert.ToString(value)) },
{ OwinConstants.RequestQueryString, new FeatureMap<IHttpRequestFeature>(feature => Utilities.RemoveQuestionMark(feature.QueryString), () => string.Empty,
(feature, value) => feature.QueryString = Utilities.AddQuestionMark(Convert.ToString(value))) },
{ OwinConstants.RequestHeaders, new FeatureMap<IHttpRequestFeature>(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeHeaderDictionary((IDictionary<string, string[]>)value)) },
{ OwinConstants.RequestBody, new FeatureMap<IHttpRequestFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
{ OwinConstants.RequestUser, new FeatureMap<IHttpAuthenticationFeature>(feature => feature.User, () => null, (feature, value) => feature.User = (ClaimsPrincipal)value) },
{ OwinConstants.ResponseStatusCode, new FeatureMap<IHttpResponseFeature>(feature => feature.StatusCode, () => 200, (feature, value) => feature.StatusCode = Convert.ToInt32(value)) },
{ OwinConstants.ResponseReasonPhrase, new FeatureMap<IHttpResponseFeature>(feature => feature.ReasonPhrase, (feature, value) => feature.ReasonPhrase = Convert.ToString(value)) },
{ OwinConstants.ResponseHeaders, new FeatureMap<IHttpResponseFeature>(feature => Utilities.MakeDictionaryStringArray(feature.Headers), (feature, value) => feature.Headers = Utilities.MakeHeaderDictionary((IDictionary<string, string[]>)value)) },
{ OwinConstants.ResponseBody, new FeatureMap<IHttpResponseFeature>(feature => feature.Body, () => Stream.Null, (feature, value) => feature.Body = (Stream)value) },
只能說還好,性能並沒有太多的損耗,粘的不全,全的自己看吧 : )
當然MS這樣做也是有用意義的,他們不太喜歡字典的方式,於是用Feature這種方式將這些內容,"強類型化了"。這對於底層的Server來說,很快能基於這組特征二次開發出一套中間件來支持ASP.NET Core,當然直接在Server內實現這樣性能也會更高。
只能說API變化有點快吧,但是對於開源,看幾天源碼就全明白了,這對於我們dotnet開發者來說,真是大大的好事兒。