[Перевод] Открытый веб-интерфейс для .NET (OWIN)

Привет, Хабр! ASP.NET Core поддерживает открытый веб-интерфейс для .NET (OWIN), а OWIN позволяет отвязывать веб-приложения от веб-серверов. Он определяет стандартный способ использования связующего ПО при обработке запросов и соответствующих ответов. Приложения ASP.NET Core и связующее ПО совместимы с приложениями, серверами и связующим ПО на базе OWIN. Подробнее об этой паре читайте под катом.

m11cewickukb1wqnmzl4vb8zjhg.jpeg

Просмотрите или скачайте образец кода

Выполнение связующего ПО в процессе ASP.NET


Поддержка OWIN со стороны ASP.NET Core развертывается в рамках пакета Microsoft.AspNetCore.Owin. Чтобы импортировать поддержку OWIN в свой проект, добавьте пакет в виде зависимости в файл project.json:

"dependencies": {
  "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
  "Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
  "Microsoft.AspNetCore.Owin": "1.0.0"
},


Связующее ПО OWIN соответствует спецификации OWIN, которая требует использовать интерфейс Func, Task> и настроить определенные ключи (например, owin.ResponseBody). Ниже приведен пример связующего ПО OWIN, которое отображает текст Hello World:

public Task OwinHello(IDictionary 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)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 и принимает IDictionary в соответствии с требованиями OWIN.

В следующем коде показано, как добавить связующее ПО OwinHello (см. выше) в процесс ASP.NET с помощью метода расширения UseOwin.

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


Вы можете настроить и другие действия для процесса OWIN.

Заголовки ответов следует менять только перед первой записью в поток ответов.

Не нужно выполнять много вызовов к UseOwin: это снижает производительность. Компоненты OWIN работают лучше, если их объединить.

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


Хостинг ASP.NET на OWIN-сервере


На OWIN-серверах можно размещать приложения ASP.NET. Один из таких серверов — Nowin, веб-сервер .NET OWIN. В пример для этой статьи мы добавили проект, который ссылается на Nowin и использует его для создания IServer, способного самостоятельно размещать 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()
                .Build();

            host.Run();
        }
    }
}


IServer — это интерфейс, который требует свойство Features и метод Start.

Start отвечает за настройку и запуск сервера. Для этого используется серия вызовов API, настраивающих адреса, которые были проанализированы из IServerAddressesFeature. Обратите внимание: конфигурация переменной _builder указывает, что запросы будет обрабатывать параметр appFunc, ранее настроенный в методе. Эта функция вызывается по каждому запросу для обработки входящих запросов.

Также мы добавим расширение 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();
            });
        }

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


Затем необходимо вызвать расширение в Program.cs, чтобы выполнить приложение ASP.NET с помощью этого пользовательского сервера:

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()
                .Build();

            host.Run();
        }
    }
}


Подробнее о серверах ASP.NET.

Выполните ASP.NET Core на OWIN-сервере и воспользуйтесь поддержкой WebSockets


Еще один способ использовать OWIN-серверы в ASP.NET Core — получить доступ к функциям типа WebSockets. Веб-сервер .NET OWIN из предыдущего примера поддерживает встроенные веб-сокеты, которые можно использовать в приложении ASP.NET Core. В примере ниже показано простое веб-приложение, которое поддерживает веб-сокеты и возвращает отправителю все данные, отправленные на серверы через веб-сокеты.

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(buffer), CancellationToken.None);

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

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

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


Этот образец настраивается с помощью того же NowinServer, что и предыдущий; единственное различие заключается в способе настройки приложения в методе Configure. Тест с помощью простого клиента веб-сокета демонстрирует приложение:

i6zy-zseh_zqc2hoxydf2tcgy7y.png

Среда OWIN


Среду OWIN можно создать с помощью HttpContext.

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


Ключи OWIN


Для передачи информации через обмен данными HTTP-запрос/ответ OWIN необходим объект IDictionary. ASP.NET Core реализует ключи, указанные ниже. См. основную спецификацию, расширения и Основные правила работы с OWIN.

Запрос данных (OWIN v1.0.0)

Ключ Значение (тип) Описание
owin.RequestScheme Строка
owin.RequestMethod Строка
owin.RequestPathBase Строка
owin.RequestPath Строка
owin.RequestQueryString Строка
owin.RequestProtocol Строка
owin.RequestHeaders IDictionary
owin.RequestBody Поток

Запрос данных (OWIN v1.1.0)

Ключ Значение (тип) Описание
owin.RequestId Строка Необязательно

Ответные данные (OWIN v1.0.0)

Ключ Значение (тип) Описание
owin.ResponseStatusCode int Необязательно
owin.ResponseReasonPhrase Строка Необязательно
owin.ResponseHeaders IDictionary
owin.ResponseBody Поток

Другие данные (OWIN v1.0.0)

Ключ Значение (тип) Описание
owin.CallCancelled CancellationToken
owin.Version Строка

Общие ключи

Ключ Значение (тип) Описание
ssl.ClientCertificate X509Certificate
ssl.LoadClientCertAsync FuncTask
server.RemoteIpAddress Строка
server.RemotePort Строка
server.LocalIpAddress Строка
server.LocalPort Строка
server.IsLocal bool
server.OnSendingHeaders ActionActionobject, object


SendFiles v0.3.0

Ключ Значение (тип) Описание
sendfile.SendAsync См. Передача подписи По запросу


Opaque v0.3.0

Ключ Значение (тип) Описание
opaque.Version Строка
opaque.Upgrade OpaqueUpgrade См. Передача подписи
opaque.Stream Поток
opaque.CallCancelled CancellationToken


WebSocket v0.3.0

Ключ Значение (тип) Описание
websocket.Version Строка
websocket.Accept WebSocketAccept См. Передача подписи
websocket.AcceptAlt Не указано
websocket.SubProtocol Строка См. шаг 5.5 в Разделе 4.2.2 RFC6455
websocket.SendAsync WebSocketSendAsync См. Передача подписи
websocket.ReceiveAsync WebSocketReceiveAsync См. Передача подписи
websocket.CloseAsync WebSocketCloseAsync См. Передача подписи
websocket.CallCancelled CancellationToken
websocket.ClientCloseStatus int Необязательно
websocket.ClientCloseDescription Строка Необязательно


Дополнительные ресурсы


  • Связующее ПО
  • Серверы

© Habrahabr.ru