ASP.NET Core 中文文檔 第三章 原理(16).NET開放Web接口(OWIN)


原文:Open Web Interface for .NET (OWIN)
作者:Steve SmithRick Anderson
翻譯:謝煬(kiler398)
校對:孟帥洋(書緣)

ASP.NET Core 支持 OWIN(即 Open Web Server Interface for .NET 的首字母縮寫),OWIN的目標是用於解耦Web Server和Web Application。此外, OWIN為中間件定義了一個標准方法用來處理單個請求以及相關聯的響應。ASP.NET Core 的程序和中間件可以和 OWIN-based 應用程序、服務器以及中間件相互交互。

章節:

查看下載示例代碼

在 ASP.NET 管道中運行 OWIN 中間件

ASP.NET Core 對於 OWIN 的支持基於 Microsoft.AspNetCore.Owin 包。你可以在你的應用程序把這個包作為一個依賴導入到你的 project.json 文件里來實現對 OWIN 支持, 如下所示:

  "dependencies": {
    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
    "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
    "Microsoft.AspNetCore.Owin": "1.0.0"                    //手動高亮
  },

OWIN 中間件遵循 OWIN 標准, OWIN 標准定義了一系列 Func<IDictionary<string, object>, Task> 需要用到的屬性接口, 並且規定了某些鍵值必須被設置 (例如 owin.ResponseBody)。 下面的簡單的中間件的例子來顯示 "Hello World":

public Task OwinHello(IDictionary<string, object> environment)
{
    string responseText = "Hello World via OWIN";
    byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);

    // OWIN Environment Keys: http://owin.org/spec/spec/owin-1.0.0.html
    var responseStream = (Stream)environment["owin.ResponseBody"];
    var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];

    responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
    responseHeaders["Content-Type"] = new string[] { "text/plain" };

    return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}

OWIN 最簡單的方法簽名是接收一個 IDictionary<string, object> 輸入參數並且返回 Task 結果。

添加 OWIN 中間件到 ASP.NET 管道是最簡單的辦法是使用 UseOwin 擴展方法完成。參考上面所示的 OwinHello 方法,將它添加到管道是一個簡單的事情:

public void Configure(IApplicationBuilder app)
{
    app.UseOwin(pipeline =>
    {
        pipeline(next => OwinHello);
    });
}

當然你也可以在 OWIN 管道中配置其他 actions 來替代。

注意
響應頭信息只能在第一次寫入響應流的時機之前修改。


注意
因為性能原因同時調用多個 UseOwin 是不被鼓勵的。 OWIN 組件如果能組合在一起將運作是最好的。

app.UseOwin(pipeline =>
{
    pipeline(next =>
    {
        // do something before
        return OwinHello;
        // do something after
    });
});

在基於 OWIN 的服務器上宿主 ASP.NET

基於 OWIN 的服務器可以宿主 ASP.NET 應用程序,Nowin 就是其中之一,一個.NET 的 OWIN Web 服務器。在本文的例子中,我已經包含一個非常簡單的項目並引用 Nowin 並用它來創建一個能夠自托管 ASP.NET 核心的一個簡單的服務器。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Owin;
using Microsoft.Extensions.Options;
using Nowin;

namespace NowinSample
{
    public class NowinServer : IServer                  //手動高亮
    {
        private INowinServer _nowinServer;
        private ServerBuilder _builder;

        public IFeatureCollection Features { get; } = new FeatureCollection();

        public NowinServer(IOptions<ServerBuilder> options)
        {
            Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
            _builder = options.Value;
        }

        public void Start<TContext>(IHttpApplication<TContext> application)
        {
            // Note that this example does not take into account of Nowin's "server.OnSendingHeaders" callback.
            // Ideally we should ensure this method is fired before disposing the context. 
            Func<IDictionary<string, object>, Task> appFunc = async env =>
            {
                // The reason for 2 level of wrapping is because the OwinFeatureCollection isn't mutable
                // so features can't be added
                var features = new FeatureCollection(new OwinFeatureCollection(env));

                var context = application.CreateContext(features);
                try
                {
                    await application.ProcessRequestAsync(context);
                }
                catch (Exception ex)
                {
                    application.DisposeContext(context, ex);
                    throw;
                }

                application.DisposeContext(context, null);
            };

            // Add the web socket adapter so we can turn OWIN websockets into ASP.NET Core compatible web sockets.
            // The calling pattern is a bit different
            appFunc = OwinWebSocketAcceptAdapter.AdaptWebSockets(appFunc);

            // Get the server addresses
            var address = Features.Get<IServerAddressesFeature>().Addresses.First();

            var uri = new Uri(address);
            var port = uri.Port;
            IPAddress ip;
            if (!IPAddress.TryParse(uri.Host, out ip))
            {
                ip = IPAddress.Loopback;
            }

            _nowinServer = _builder.SetAddress(ip)
                                    .SetPort(port)
                                    .SetOwinApp(appFunc)
                                    .Build();
            _nowinServer.Start();
        }

        public void Dispose()
        {
            _nowinServer?.Dispose();
        }
    }
}

IServer 是一個需要 Features 屬性和 Start 方法的接口。

Start 的職責是配置和啟動服務器,在本示例中是通過一系列 fluent API 調用IServerAddressesFeature硬編碼服務器地址來監聽請求。注意 fluent 的 builder 變量指定了請求會被方法 appFunc 所處理。Func 方法在每一個請求被處理前調用。

我們同樣會添加 IWebHostBuilder 擴展來使得 Nowin 服務器易於添加和配置。

using System;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection;
using Nowin;
using NowinSample;

namespace Microsoft.AspNetCore.Hosting
{
    public static class NowinWebHostBuilderExtensions
    {
        public static IWebHostBuilder UseNowin(this IWebHostBuilder builder)        //手動高亮
        {
            return builder.ConfigureServices(services =>
            {
                services.AddSingleton<IServer, NowinServer>();
            });
        }

        public static IWebHostBuilder UseNowin(this IWebHostBuilder builder, Action<ServerBuilder> configure)
        {
            builder.ConfigureServices(services =>
            {
                services.Configure(configure);
            });
            return builder.UseNowin();
        }
    }
}

上述操作就緒以后,所有的需要使用自定義服務器運行 ASP.NET 應用程序的設置都在下面的 project.json 文件的命令中:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;

namespace NowinSample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = new WebHostBuilder()
                .UseNowin()                                     //手動高亮
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();

            host.Run();
        }
    }
}

了解更多關於 ASP.NET Servers

在 OWIN-based 服務器上運行 ASP.NET Core 並使用 WebSockets 支持

如何基於OWIN的服務器功能,可以通過ASP.NET核心加以利用另一個例子是獲得像WebSockets的功能。在前面的例子中使用的.NET OWIN Web服務器具有內置的網絡插座,可通過一個ASP.NET的核心應用加以利用的支持。下面的例子顯示了支持網絡套接字和簡單的回顯然后直接通過WebSockets發送到服務器的任何一個簡單的Web應用程序。

    public class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            app.Use(async (context, next) =>
            {
                if (context.WebSockets.IsWebSocketRequest)            //手動高亮
                {                                                     //手動高亮
                    WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();     //手動高亮
                    await EchoWebSocket(webSocket);                   //手動高亮
                }
                else
                {
                    await next();
                }
            });

            app.Run(context =>
            {
                return context.Response.WriteAsync("Hello World");
            });
        }

        private async Task EchoWebSocket(WebSocket webSocket)
        {
            byte[] buffer = new byte[1024];
            WebSocketReceiveResult received = await webSocket.ReceiveAsync(
                new ArraySegment<byte>(buffer), CancellationToken.None);

            while (!webSocket.CloseStatus.HasValue)
            {
                // Echo anything we receive
                await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, received.Count), 
                    received.MessageType, received.EndOfMessage, CancellationToken.None);

                received = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), 
                    CancellationToken.None);
            }

            await webSocket.CloseAsync(webSocket.CloseStatus.Value, 
                webSocket.CloseStatusDescription, CancellationToken.None);
        }
    }
}

這個 例子 和前一個配置一樣使用相同 NowinServer 唯一的區別是在該應用程序是如何在其 Configure 方法是如何配置的。用 一個簡單的 websocket 客戶端的演示實際效果:

OWIN 鍵值

OWIN 依賴一個 IDictionary<string,object> 對象用來在一個完整的 HTTP 請求/響應交互中通訊信息。ASP.NET Core 實現所有的 OWIN 規范中列出的要求的必需和可選的以及自身實現的鍵。在OWIN規范不要求任何鍵是可選的,並且可以僅在某些情況下可以使用。 在使用 OWIN 鍵的時候,參閱 OWIN Key Guidelines and Common Keys 是一個好習慣。

請求數據 (OWIN v1.0.0)

值 (類型) 描述
owin.RequestScheme String
owin.RequestMethod String
owin.RequestPathBase String
owin.RequestPath String
owin.RequestQueryString String
owin.RequestProtocol String
owin.RequestHeaders IDictionary<string,string[]>
owin.RequestBody Stream

請求數據 (OWIN v1.1.0)

值 (類型) 描述
owin.RequestId String 可選項

響應數據 (OWIN v1.0.0)

值 (類型) 描述
owin.ResponseStatusCode int 可選項
owin.ResponseReasonPhrase String 可選項
owin.ResponseHeaders IDictionary<string,string[]>
owin.ResponseBody Stream

其他數據 (OWIN v1.0.0)

值 (類型) 描述
owin.CallCancelled CancellationToken
owin.Version String

通用鍵值

值 (類型) 描述
ssl.ClientCertificate X509Certificate
ssl.LoadClientCertAsync Func<Task>
server.RemoteIpAddress String
server.RemotePort String
server.LocalIpAddress String
server.LocalPort String
server.IsLocal bool
server.OnSendingHeaders Action<Action<object>,object>

發送文件 v0.3.0

值 (類型) 描述
sendfile.SendAsync 參考 delegate signature 每請求

Opaque v0.3.0

值 (類型) 描述
opaque.Version String
opaque.Upgrade OpaqueUpgrade 參考 delegate signature
opaque.Stream Stream
opaque.CallCancelled CancellationToken

WebSocket v0.3.0

值 (類型) 描述
websocket.Version String
websocket.Accept WebSocketAccept 參考 delegate signature
websocket.AcceptAlt 沒有規定
websocket.SubProtocol String 參考 RFC6455 Section 4.2.2 Step 5.5
websocket.SendAsync WebSocketSendAsync 參考 delegate signature
websocket.ReceiveAsync WebSocketReceiveAsync 參考 delegate signature
websocket.CloseAsync WebSocketCloseAsync 參考 delegate signature
websocket.CallCancelled CancellationToken
websocket.ClientCloseStatus int 可選項
websocket.ClientCloseDescription String 可選項

附錄資源

返回目錄


免責聲明!

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



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