ASP.NET Core 高階(一)【.NET 的開放 Web 介面 (OWIN)】

風靈使發表於2019-02-20

ASP.NET Core 中 .NET 的開放 Web 介面 (OWIN)

ASP.NET Core 支援 .NET 的開放 Web 介面 (OWIN)。 OWIN 允許 Web 應用從 Web 伺服器分離。 它定義了在管道中使用中介軟體來處理請求和相關響應的標準方法。 ASP.NET Core 應用程式和中介軟體可以與基於 OWIN 的應用程式、伺服器和中介軟體進行互操作。

OWIN 提供了一個分離層,可一起使用具有不同物件模型的兩個框架。 Microsoft.AspNetCore.Owin 包提供了兩個介面卡實現:

此方法可將 ASP.NET Core 託管在相容 OWIN 的伺服器/主機上,或在 ASP.NET Core 上執行其他相容 OWIN 的元件。

備註
使用這些介面卡會帶來效能成本。 僅使用 ASP.NET Core 元件的應用不應使用 Microsoft.AspNetCore.Owin 包或介面卡。

ASP.NET Core 管道中執行 OWIN 中介軟體

ASP.NET Core 的 OWIN 支援作為 Microsoft.AspNetCore.Owin 包的一部分進行部署。 可通過安裝此包將 OWIN 支援匯入到專案中。

OWIN 中介軟體符合 OWIN 規範,該規範要求使用 Func<IDictionary<string, object>, Task> 介面,並設定特定的鍵(如 owin.ResponseBody)。 以下簡單的 OWIN 中介軟體顯示“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);
}

示例簽名返回 Task,並接受 OWIN 所要求的 IDictionary<string, object>

以下程式碼顯示瞭如何使用 UseOwin 擴充套件方法將 OwinHello 中介軟體(如上所示)新增到 ASP.NET Core 管道。

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

可配置在 OWIN 管道中要進行的其他操作。

備註
響應標頭只能在首次寫入響應流之前進行修改。

備註
由於效能原因,不建議多次呼叫 UseOwin。 組合在一起時 OWIN 元件的效能最佳。

app.UseOwin(pipeline =>
{
    pipeline(async (next) =>
    {
        // do something before
        await OwinHello(new OwinEnvironment(HttpContext));
        // do something after
    });
});

在基於 OWIN 的伺服器中使用 ASP.NET Core 託管

基於 OWIN 的伺服器可託管 ASP.NET Core 應用。 Nowin(.NET OWIN Web 伺服器)屬於這種伺服器。 本文的示例中包含了一個引用 Nowin 的專案,並使用它建立了一個自託管 ASP.NET Core 的 IServer

[!code-csharp]

IServer 是需要 Features 屬性和 Start 方法的介面。

Start 負責配置和啟動伺服器,在此情況下,此操作通過一系列 Fluent API 呼叫完成,這些呼叫設定從 IServerAddressesFeature 分析的地址。 請注意,_builder 變數的 Fluent 配置指定請求將由方法中之前定義的 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();
        }
    }
}

完成此操作後,呼叫 Program.cs 中的擴充套件以使用此自定義伺服器執行 ASP.NET Core 應用:

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 Core 伺服器的更多資訊。

在基於 OWIN 的伺服器上執行 ASP.NET Core 並使用其 WebSocket 支援

ASP.NET Core 如何利用基於 OWIN 的伺服器功能的另一個示例是訪問 WebSocket 等功能。 前面示例中使用的 .NET OWIN Web 伺服器支援內建的 Web 套接字,可由 ASP.NET Core 應用程式利用。 下面的示例顯示了簡單的 Web 應用,它支援 Web 套接字並回顯通過 WebSocket 傳送到伺服器的所有內容。

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 環境

可使用 HttpContext 來構造 OWIN 環境。

   var environment = new OwinEnvironment(HttpContext);
   var features = new OwinFeatureCollection(environment);

OWIN 鍵

OWIN 依賴於 IDictionary<string,object> 物件,以在整個 HTTP請求/響應交換中傳達資訊。 ASP.NET Core 實現以下所列的鍵。 請參閱主規範、擴充套件OWIN Key Guidelines and Common Keys(OWIN 鍵指南和常用鍵)。

請求資料 (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 Optional

響應資料 (OWIN v1.0.0)

值(型別) 說明
owin.ResponseStatusCode int Optional
owin.ResponseReasonPhrase String Optional
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>

SendFiles v0.3.0

值(型別) 說明
sendfile.SendAsync 請參閱委託簽名 每請求

Opaque v0.3.0

值(型別) 說明
opaque.Version String
opaque.Upgrade OpaqueUpgrade 請參閱委託簽名
opaque.Stream Stream
opaque.CallCancelled CancellationToken

WebSocket v0.3.0

值(型別) 說明
websocket.Version String
websocket.Accept WebSocketAccept 請參閱委託簽名
websocket.AcceptAlt 非規範
websocket.SubProtocol String 請參閱 RFC6455 4.2.2 節步驟 5.5
websocket.SendAsync WebSocketSendAsync 請參閱委託簽名
websocket.ReceiveAsync WebSocketReceiveAsync 請參閱委託簽名
websocket.CloseAsync WebSocketCloseAsync 請參閱委託簽名
websocket.CallCancelled CancellationToken
websocket.ClientCloseStatus int Optional
websocket.ClientCloseDescription String Optional

相關文章