Interfaz web abierta para .NET (OWIN) con ASP.NET Core

Por Steve Smith y Rick Anderson

ASP.NET Core:

  • Es compatible con la interfaz web abierta para .NET (OWIN).
  • Tiene reemplazos compatibles con .NET Core para las bibliotecas Microsoft.Owin.* (Katana).

OWIN permite que las aplicaciones web se desacoplen de los servidores web. Define una manera estándar para usar software intermedio en una canalización a fin de controlar las solicitudes y las respuestas asociadas. El software intermedio y las aplicaciones de ASP.NET Core pueden interoperar con aplicaciones, servidores y software intermedio basados en OWIN.

OWIN proporciona una capa de desacoplamiento que permite que dos marcos de trabajo con modelos de objetos dispares se usen juntos. El paquete Microsoft.AspNetCore.Owin proporciona dos implementaciones del adaptador:

  • De ASP.NET Core a OWIN
  • De OWIN a ASP.NET Core

Esto permite que ASP.NET Core se hospede sobre un servidor/host compatible con OWIN, o bien que otros componentes compatibles con OWIN se ejecuten sobre ASP.NET Core.

Nota

El uso de estos adaptadores conlleva un costo de rendimiento. Las aplicaciones que solo usan componentes de ASP.NET Core no deben usar el paquete o adaptadores de Microsoft.AspNetCore.Owin.

Consulta o descarga el código de ejemplo (cómo descargarlo)

Ejecución de software intermedio de OWIN en la canalización de ASP.NET Core

La compatibilidad con OWIN de ASP.NET Core se implementa como parte del paquete Microsoft.AspNetCore.Owin. Puedes importar compatibilidad con OWIN en el proyecto mediante la instalación de este paquete.

El software intermedio de OWIN cumple la especificación de OWIN, que requiere una interfaz Func<IDictionary<string, object>, Task> y el establecimiento de determinadas claves (como owin.ResponseBody). En el siguiente software intermedio simple de OWIN se muestra "Hello World":

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

    // OWIN Environment Keys: https://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);
}

La firma de ejemplo devuelve un valor Task y acepta un valor IDictionary<string, object>, según los requisitos de OWIN.

En el código siguiente se muestra cómo agregar el software intermedio OwinHello (mostrado arriba) a la canalización ASP.NET Core con el método de extensión UseOwin.

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

Puedes configurar la realización de otras acciones en la canalización de OWIN.

Nota

Los encabezados de respuesta solo deben modificarse antes de la primera escritura en la secuencia de respuesta.

Nota

No se recomienda la realización de varias llamadas a UseOwin por motivos de rendimiento. Los componentes de OWIN funcionarán mejor si se agrupan.

app.UseOwin(pipeline =>
{
    pipeline(next =>
    {
        return async environment =>
        {
            // Do something before.
            await next(environment);
            // Do something after.
        };
    });
});

Ejecución de ASP.NET Core en un servidor basado en OWIN y uso de su compatibilidad con WebSockets

Otro ejemplo de la manera en que ASP.NET Core puede aprovechar las características de los servidores basados en OWIN es el acceso a funciones como WebSockets. El servidor web de OWIN de .NET que se usa en el ejemplo anterior cuenta con compatibilidad integrada con WebSockets, cuyas ventajas puede aprovechar la aplicación ASP.NET Core. En el ejemplo siguiente se muestra una aplicación web simple que admite WebSockets y devuelve todo lo que se envía al servidor a través de 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);
    }
}

Entorno de OWIN

Puedes construir un entorno de OWIN por medio de HttpContext.


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

Claves de OWIN

OWIN depende de un objeto IDictionary<string,object> para comunicar información mediante un intercambio de solicitud/respuesta HTTP. ASP.NET Core implementa las claves que se enumeran a continuación. Consulta las extensiones de la especificación principal y las directrices principales y claves comunes de OWIN.

Datos de solicitud (OWIN v1.0.0)

Key Valor (tipo) Descripción
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

Datos de solicitud (OWIN v1.1.0)

Key Valor (tipo) Descripción
owin.RequestId String Optional

Datos de respuesta (OWIN v1.0.0)

Key Valor (tipo) Descripción
owin.ResponseStatusCode int Optional
owin.ResponseReasonPhrase String Optional
owin.ResponseHeaders IDictionary<string,string[]>
owin.ResponseBody Stream

Otros datos (OWIN v1.0.0)

Key Valor (tipo) Descripción
owin.CallCancelled CancellationToken
owin.Version String

Claves comunes

Key Valor (tipo) Descripción
ssl.ClientCertificate X509Certificate
ssl.LoadClientCertAsync Func<Task>
server.RemoteIpAddress String
server.RemotePort String
server.LocalIpAddress String
server.LocalPort String
server.OnSendingHeaders Action<Action<object>,object>

SendFiles v0.3.0

Key Valor (tipo) Descripción
sendfile.SendAsync Vea Delegate signature (Signatura de delegado) Por solicitud

Opaque v0.3.0

Key Valor (tipo) Descripción
opaque.Version String
opaque.Upgrade OpaqueUpgrade Vea Delegate signature (Signatura de delegado)
opaque.Stream Stream
opaque.CallCancelled CancellationToken

WebSocket v0.3.0

Key Valor (tipo) Descripción
websocket.Version String
websocket.Accept WebSocketAccept Vea Delegate signature (Signatura de delegado)
websocket.AcceptAlt Sin especificaciones
websocket.SubProtocol String Vea la sección 4.2.2 de RFC6455, paso 5.5
websocket.SendAsync WebSocketSendAsync Vea Delegate signature (Signatura de delegado)
websocket.ReceiveAsync WebSocketReceiveAsync Vea Delegate signature (Signatura de delegado)
websocket.CloseAsync WebSocketCloseAsync Vea Delegate signature (Signatura de delegado)
websocket.CallCancelled CancellationToken
websocket.ClientCloseStatus int Optional
websocket.ClientCloseDescription String Optional

Recursos adicionales