ASP.NET Core 中 .NET 的开放 Web 接口 (OWIN)Open Web Interface for .NET (OWIN) with ASP.NET Core

作者:Steve SmithRick AndersonBy Steve Smith and Rick Anderson

ASP.NET Core 支持 .NET 的开放 Web 接口 (OWIN)。ASP.NET Core supports the Open Web Interface for .NET (OWIN). OWIN 允许 Web 应用从 Web 服务器分离。OWIN allows web apps to be decoupled from web servers. 它定义了在管道中使用中间件来处理请求和相关响应的标准方法。It defines a standard way for middleware to be used in a pipeline to handle requests and associated responses. ASP.NET Core 应用程序和中间件可以与基于 OWIN 的应用程序、服务器和中间件进行互操作。ASP.NET Core applications and middleware can interoperate with OWIN-based applications, servers, and middleware.

OWIN 提供了一个分离层,可一起使用具有不同对象模型的两个框架。OWIN provides a decoupling layer that allows two frameworks with disparate object models to be used together. Microsoft.AspNetCore.Owin 包提供了两个适配器实现:The Microsoft.AspNetCore.Owin package provides two adapter implementations:

  • ASP.NET Core 到 OWINASP.NET Core to OWIN
  • OWIN 到 ASP.NET CoreOWIN to ASP.NET Core

此方法可将 ASP.NET Core 托管在兼容 OWIN 的服务器/主机上,或在 ASP.NET Core 上运行其他兼容 OWIN 的组件。This allows ASP.NET Core to be hosted on top of an OWIN compatible server/host or for other OWIN compatible components to be run on top of ASP.NET Core.

备注

使用这些适配器会带来性能成本。Using these adapters comes with a performance cost. 仅使用 ASP.NET Core 组件的应用不应使用 Microsoft.AspNetCore.Owin 包或适配器。Apps using only ASP.NET Core components shouldn't use the Microsoft.AspNetCore.Owin package or adapters.

查看或下载示例代码如何下载View or download sample code (how to download)

在 ASP.NET Core 管道中运行 OWIN 中间件Running OWIN middleware in the ASP.NET Core pipeline

ASP.NET Core 的 OWIN 支持作为 Microsoft.AspNetCore.Owin 包的一部分进行部署。ASP.NET Core's OWIN support is deployed as part of the Microsoft.AspNetCore.Owin package. 可通过安装此包将 OWIN 支持导入到项目中。You can import OWIN support into your project by installing this package.

OWIN 中间件符合 OWIN 规范,该规范要求使用 Func<IDictionary<string, object>, Task> 接口,并设置特定的键(如 owin.ResponseBody)。OWIN middleware conforms to the OWIN specification, which requires a Func<IDictionary<string, object>, Task> interface, and specific keys be set (such as owin.ResponseBody). 以下简单的 OWIN 中间件显示“Hello World”:The following simple OWIN middleware displays "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>The sample signature returns a Task and accepts an IDictionary<string, object> as required by OWIN.

以下代码显示了如何使用 UseOwin 扩展方法将 OwinHello 中间件(如上所示)添加到 ASP.NET Core 管道。The following code shows how to add the OwinHello middleware (shown above) to the ASP.NET Core pipeline with the UseOwin extension method.

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

可配置在 OWIN 管道中要进行的其他操作。You can configure other actions to take place within the OWIN pipeline.

备注

响应标头只能在首次写入响应流之前进行修改。Response headers should only be modified prior to the first write to the response stream.

备注

由于性能原因,不建议多次调用 UseOwinMultiple calls to UseOwin is discouraged for performance reasons. 组合在一起时 OWIN 组件的性能最佳。OWIN components will operate best if grouped together.

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

在基于 OWIN 的服务器中使用 ASP.NET Core 托管Using ASP.NET Core Hosting on an OWIN-based server

基于 OWIN 的服务器可托管 ASP.NET Core 应用。OWIN-based servers can host ASP.NET Core apps. Nowin(.NET OWIN Web 服务器)属于这种服务器。One such server is Nowin, a .NET OWIN web server. 本文的示例中包含了一个引用 Nowin 的项目,并使用它创建了一个自托管 ASP.NET Core 的 IServerIn the sample for this article, I've included a project that references Nowin and uses it to create an IServer capable of self-hosting 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();
        }
    }
}

IServer 是需要 Features 属性和 Start 方法的接口。IServer is an interface that requires a Features property and a Start method.

Start 负责配置和启动服务器,在此情况下,此操作通过一系列 Fluent API 调用完成,这些调用设置从 IServerAddressesFeature 分析的地址。Start is responsible for configuring and starting the server, which in this case is done through a series of fluent API calls that set addresses parsed from the IServerAddressesFeature. 请注意,_builder 变量的 Fluent 配置指定请求将由方法中之前定义的 appFunc 来处理。Note that the fluent configuration of the _builder variable specifies that requests will be handled by the appFunc defined earlier in the method. 对于每个请求,都会调用此 Func 以处理传入请求。This Func is called on each request to process incoming requests.

我们还将添加一个 IWebHostBuilder 扩展,以便添加和配置 Nowin 服务器。We'll also add an IWebHostBuilder extension to make it easy to add and configure the Nowin server.

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 应用:With this in place, invoke the extension in Program.cs to run an ASP.NET Core app using this custom server:

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 服务器的更多信息。Learn more about ASP.NET Core Servers.

在基于 OWIN 的服务器上运行 ASP.NET Core 并使用其 WebSocket 支持Run ASP.NET Core on an OWIN-based server and use its WebSockets support

ASP.NET Core 如何利用基于 OWIN 的服务器功能的另一个示例是访问 WebSocket 等功能。Another example of how OWIN-based servers' features can be leveraged by ASP.NET Core is access to features like WebSockets. 前面示例中使用的 .NET OWIN Web 服务器支持内置的 Web 套接字,可由 ASP.NET Core 应用程序利用。The .NET OWIN web server used in the previous example has support for Web Sockets built in, which can be leveraged by an ASP.NET Core application. 下面的示例显示了简单的 Web 应用,它支持 Web 套接字并回显通过 WebSocket 发送到服务器的所有内容。The example below shows a simple web app that supports Web Sockets and echoes back everything sent to the server through WebSockets.

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 方法中配置应用程序。This sample is configured using the same NowinServer as the previous one - the only difference is in how the application is configured in its Configure method. 使用简单的 websocket 客户端的测试演示应用程序:A test using a simple websocket client demonstrates the application:

Web 套接字测试客户端

OWIN 环境OWIN environment

可使用 HttpContext 来构造 OWIN 环境。You can construct an OWIN environment using the HttpContext.


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

OWIN 键OWIN keys

OWIN 依赖于 IDictionary<string,object> 对象,以在整个 HTTP请求/响应交换中传达信息。OWIN depends on an IDictionary<string,object> object to communicate information throughout an HTTP Request/Response exchange. ASP.NET Core 实现以下所列的键。ASP.NET Core implements the keys listed below. 请参阅主规范、扩展OWIN Key Guidelines and Common Keys(OWIN 键指南和常用键)。See the primary specification, extensions, and OWIN Key Guidelines and Common Keys.

请求数据 (OWIN v1.0.0)Request data (OWIN v1.0.0)

Key 值(类型)Value (type) 说明Description
owin.RequestSchemeowin.RequestScheme String
owin.RequestMethodowin.RequestMethod String
owin.RequestPathBaseowin.RequestPathBase String
owin.RequestPathowin.RequestPath String
owin.RequestQueryStringowin.RequestQueryString String
owin.RequestProtocolowin.RequestProtocol String
owin.RequestHeadersowin.RequestHeaders IDictionary<string,string[]>
owin.RequestBodyowin.RequestBody Stream

请求数据 (OWIN v1.1.0)Request data (OWIN v1.1.0)

Key 值(类型)Value (type) 说明Description
owin.RequestIdowin.RequestId String OptionalOptional

响应数据 (OWIN v1.0.0)Response data (OWIN v1.0.0)

Key 值(类型)Value (type) 说明Description
owin.ResponseStatusCodeowin.ResponseStatusCode int OptionalOptional
owin.ResponseReasonPhraseowin.ResponseReasonPhrase String OptionalOptional
owin.ResponseHeadersowin.ResponseHeaders IDictionary<string,string[]>
owin.ResponseBodyowin.ResponseBody Stream

其他数据 (OWIN v1.0.0)Other data (OWIN v1.0.0)

Key 值(类型)Value (type) 说明Description
owin.CallCancelledowin.CallCancelled CancellationToken
owin.Versionowin.Version String

常用键Common keys

Key 值(类型)Value (type) 说明Description
ssl.ClientCertificatessl.ClientCertificate X509Certificate
ssl.LoadClientCertAsyncssl.LoadClientCertAsync Func<Task>
server.RemoteIpAddressserver.RemoteIpAddress String
server.RemotePortserver.RemotePort String
server.LocalIpAddressserver.LocalIpAddress String
server.LocalPortserver.LocalPort String
server.IsLocalserver.IsLocal bool
server.OnSendingHeadersserver.OnSendingHeaders Action<Action<object>,object>

SendFiles v0.3.0SendFiles v0.3.0

Key 值(类型)Value (type) 说明Description
sendfile.SendAsyncsendfile.SendAsync 请参阅委托签名See delegate signature 每请求Per Request

Opaque v0.3.0Opaque v0.3.0

Key 值(类型)Value (type) 说明Description
opaque.Versionopaque.Version String
opaque.Upgradeopaque.Upgrade OpaqueUpgrade 请参阅委托签名See delegate signature
opaque.Streamopaque.Stream Stream
opaque.CallCancelledopaque.CallCancelled CancellationToken

WebSocket v0.3.0WebSocket v0.3.0

Key 值(类型)Value (type) 说明Description
websocket.Versionwebsocket.Version String
websocket.Acceptwebsocket.Accept WebSocketAccept 请参阅委托签名See delegate signature
websocket.AcceptAltwebsocket.AcceptAlt 非规范Non-spec
websocket.SubProtocolwebsocket.SubProtocol String 请参阅 RFC6455 4.2.2 节步骤 5.5See RFC6455 Section 4.2.2 Step 5.5
websocket.SendAsyncwebsocket.SendAsync WebSocketSendAsync 请参阅委托签名See delegate signature
websocket.ReceiveAsyncwebsocket.ReceiveAsync WebSocketReceiveAsync 请参阅委托签名See delegate signature
websocket.CloseAsyncwebsocket.CloseAsync WebSocketCloseAsync 请参阅委托签名See delegate signature
websocket.CallCancelledwebsocket.CallCancelled CancellationToken
websocket.ClientCloseStatuswebsocket.ClientCloseStatus int OptionalOptional
websocket.ClientCloseDescriptionwebsocket.ClientCloseDescription String OptionalOptional

其他资源Additional resources