警告

This page documents version 1.0.0-rc1 and has not yet been updated for version 1.0.0

OWIN

By Steve Smith

ASP.NET Core supports OWIN, the Open Web Interface for .NET, which allows web applications to be decoupled from web servers. In addition, OWIN defines a standard way for middleware to be used in a pipeline to handle individual requests and associated responses. ASP.NET Core applications and middleware can interoperate with OWIN-based applications, servers, and middleware.

View or download sample code

Running OWIN middleware in the ASP.NET pipeline

ASP.NET Core’s OWIN support is deployed as part of the Microsoft.AspNetCore.Owin package. You can import OWIN support into your project by adding this package as a dependency in your project.json file, as shown here:

  "dependencies": {
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.Owin": "1.0.0-rc1-final"
  },

OWIN middleware conform to the OWIN specification, which defines a Properties IDictionary<string, object> interface that must be used, and also requires certain keys be set (such as owin.ResponseBody). We can construct a very simple example of middleware that follows the OWIN specification to display “Hello World”, as shown here:

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/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);
}

In the above example, notice that the method returns a Task and accepts an IDictionary<string, object> as required by OWIN. Within the method, this parameter is used to retrieve the owin.ResponseBody and owin.ResponseHeaders objects from the environment dictionary. Once the headers are set appropriately for the content being returned, a task representing the asynchronous write to the response stream is returned.

Adding OWIN middleware to the ASP.NET pipeline is most easily done using the UseOwin extension method. Given the OwinHello method shown above, adding it to the pipeline is a simple matter:

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

You can of course configure other actions to take place within the OWIN pipeline. Remember that response headers should only be modified prior to the first write to the response stream, so configure your pipeline accordingly.

注解

Multiple calls to UseOwin is discouraged for performance reasons. OWIN components will operate best if grouped together.

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

注解

The OWIN support in ASP.NET Core is an evolution of the work that was done for the Katana project. Katana’s IAppBuilder component has been replaced by IApplicationBuilder, but if you have existing Katana-based middleware, you can use it within your ASP.NET Core application through the use of a bridge, as shown in the Owin.IAppBuilderBridge example on GitHub.

Using ASP.NET Hosting on an OWIN-based server

OWIN-based servers can host ASP.NET applications, since ASP.NET conforms to the OWIN specification. One such server is Nowin, a .NET OWIN web server. In the sample for this article, I’ve included a very simple project that references Nowin and uses it to create a simple server capable of self-hosting ASP.NET Core.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting.Server;
using Microsoft.AspNet.Owin;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNet.Http.Features;
using Nowin;

namespace NowinSample
{
    public class NowinServerFactory : IServerFactory
    {
        private Func<IFeatureCollection, Task> _callback;

        private Task HandleRequest(IDictionary<string, object> env)
        {
            return _callback(new FeatureCollection(new OwinFeatureCollection(env)));
        }

        public IFeatureCollection Initialize(IConfiguration configuration)
        {
            var builder = ServerBuilder.New()
                        .SetAddress(IPAddress.Any)
                        .SetPort(5000)
                        .SetOwinApp(HandleRequest);

            var serverFeatures = new FeatureCollection();
            serverFeatures.Set<INowinServerInformation>(new NowinServerInformation(builder));
            return serverFeatures;
        }

        public IDisposable Start(IFeatureCollection serverFeatures, 
                                 Func<IFeatureCollection, Task> application)
        {
            var information = serverFeatures.Get<INowinServerInformation>();
            _callback = application;
            INowinServer server = information.Builder.Build();
            server.Start();
            return server;
        }

        private class NowinServerInformation : INowinServerInformation
        {
            public NowinServerInformation(ServerBuilder builder)
            {
                Builder = builder;
            }

            public ServerBuilder Builder { get; private set; }

            public string Name
            {
                get
                {
                    return "Nowin";
                }
            }
        }
    }
}

IServerFactory is an interface that requires an Initialize and a Start method. Initialize must return an instance of IFeatureCollection, which we populate with a INowinServerInformation that includes the server’s name (the specific implementation may provide additional functionality). In this example, the NowinServerInformation class is defined as a private class within the factory, and is returned by Initialize as required.

Initialize is responsible for configuring the server, which in this case is done through a series of fluent API calls that hard code the server to listen for requests (to any IP address) on port 5000. Note that the final line of the fluent configuration of the builder variable specifies that requests will be handled by the private method HandleRequest.

Start is called after Initialize and accepts the the IFeatureCollection created by Initialize, and a callback of type Func<IFeatureCollection, Task>. This callback is assigned to a local field and is ultimately called on each request from within the private HandleRequest method (which was wired up in Initialize).

With this in place, all that’s required to run an ASP.NET application using this custom server is the following command in project.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "version": "1.0.0-*",
  "compilationOptions": {
    "emitEntryPoint": true
  },
  
  "dependencies": {
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.Owin": "1.0.0-rc1-final",
    "Nowin": "0.22.0"
  },

  "commands": {
      "web": "Microsoft.AspNet.Hosting --server NowinSample"
  },

When run, this command will search for a package called “NowinSample” that contains an implementation of IServerFactory. If it finds one, it will initialize and start the server as detailed above. Learn more about the built-in ASP.NET Servers.

Run ASP.NET Core on an OWIN-based server and use its WebSockets support

Another example of how OWIN-based servers’ features can be leveraged by ASP.NET Core is access to features like WebSockets. 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. The example below shows a simple web application that supports Web Sockets and simply echos back anything sent to the server via WebSockets.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    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);
        }

        // Entry point for the application.        
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

This sample is configured using the same NowinServerFactory as the previous one - the only difference is in how the application is configured in its Configure method. A simple test using a simple websocket client demonstrates that the application works as expected:

../_images/websocket-test.png

OWIN keys

OWIN depends heavily on an IDictionary<string,object> used to communicate information throughout an HTTP Request/Response exchange. ASP.NET Core implements all of the required and optional keys outlined in the OWIN specification, as well as some of its own. Note that any keys not required in the OWIN specification are optional and may only be used in some scenarios. When working with OWIN keys, it’s a good idea to review the list of OWIN Key Guidelines and Common Keys

Request Data (OWIN v1.0.0)

Key Value (type) Description
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  

Request Data (OWIN v1.1.0)

Key Value (type) Description
owin.RequestId String Optional

Response Data (OWIN v1.0.0)

Key Value (type) Description
owin.ResponseStatusCode int Optional
owin.ResponseReasonPhrase String Optional
owin.ResponseHeaders IDictionary<string,string[]>  
owin.ResponseBody Stream  

Other Data (OWIN v1.0.0)

Key Value (type) Description
owin.CallCancelled CancellationToken  
owin.Version String  

Common Keys

Key Value (type) Description
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

Key Value (type) Description
sendfile.SendAsync See delegate signature Per Request

Opaque v0.3.0

Key Value (type) Description
opaque.Version String  
opaque.Upgrade OpaqueUpgrade See delegate signature
opaque.Stream Stream  
opaque.CallCancelled CancellationToken  

WebSocket v0.3.0

Key Value (type) Description
websocket.Version String  
websocket.Accept WebSocketAccept See delegate signature.
websocket.AcceptAlt   Non-spec
websocket.SubProtocol String See RFC6455 Section 4.2.2 Step 5.5
websocket.SendAsync WebSocketSendAsync See delegate signature.
websocket.ReceiveAsync WebSocketReceiveAsync See delegate signature.
websocket.CloseAsync WebSocketCloseAsync See delegate signature.
websocket.CallCancelled CancellationToken  
websocket.ClientCloseStatus int Optional
websocket.ClientCloseDescription String Optional

Summary

ASP.NET Core has built-in support for the OWIN specification, providing compatibility to run ASP.NET Core applications within OWIN-based servers as well as supporting OWIN-based middleware within ASP.NET Core servers.