天天看點

Owin中間件動手做

摘要:本文目的是了解Owin基本原理。講述如何從控制台建立一個自宿主的OwinHost,然後再編寫一兩個中間件

準備工作

首先通過VisualStudio建立一個控制台應用

然後添加Owin的Nuget包引用

需要的包如下

Owin
Microsoft.Owin
Microsoft.Owin.Hosting
Microsoft.Owin.Host.HttpListener           

準備工作到此結束

編寫OwinStartup類

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Run(HandleRequest);
    }
    static Task HandleRequest(IOwinContext context)
    {
        context.Response.ContentType = "text/plain";
        return context.Response.WriteAsync("Hello world from myOwin");
    }
}           

當OwinHost運作時,會首先加載Startup類,

Configuration

方法是必須有的,在

Configuration

方法中向Owin管道插入中間件,所有的請求都是中間件處理。

在這個

Configuration

app.Run(HandleRequest);

方法的作用是向管道中添加一個沒有後續中間件的中間件,一般來講一個Owin管道中有許多中間件,不同的中間件處理不同的事情,在處理結束後選擇性的調用後面的中間件,例如某個身份驗證中間件可以在驗證失敗時結束請求。而

app.Run

方法就是插如一個沒有後續事項的中間件。稍後我們會編寫正常的中間件。

這個中間件做的事很簡單,就是向響應寫入一個字元串,無論請求是什麼結果都是一樣的。

在Main方法中啟動Host

static void Main(string[] args)
{
    var url = "http://localhost:8080/";
    var opt = new StartOptions(url);

    using (WebApp.Start<Startup>(opt))
    {
        Console.WriteLine("Server run at " + url + " , press Enter to exit.");
        Console.ReadLine();
    }
}           

StartOptions

類用來指定一些啟動參數,最少應該指定一個url,這裡一并指定了使用8080端口

啟動程式控制台輸出如下

Server run at http://localhost:8080/ , press Enter to exit.           

用浏覽器打開

http://localhost:8080/

效果如下:

`Hello world from myOwin`           

嘗試更改路徑你得到的始終是一個結果

你可以嘗試将

Configuration

中的代碼注釋掉,在運作程式,這是通路将得到空頁面,Http代碼也将是404,因為Owin管道中沒有中間件處理請求。

編寫中間件

我們編寫一個名為Ding的中間件

public class DingMiddleware : OwinMiddleware
{
    public DingMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override Task Invoke(IOwinContext context)
    {
        if (context.Request.Path.Value.Equals("/home/index"))
        {
            context.Response.Write("hello world from home/index");
        }
        if (Next != null)
        {
            return Next.Invoke(context);
        }
        return Task.CompletedTask;
    }
}           

這個中間件在檢測到通路路徑是

/home/index

時向Response中寫入一句話,然後檢測是否有下一個中間件,如果有就調用。

添加中間件到

Configuration

可以直接在

Configuration

中加入

app.Use<DingMiddleware>()

來插入中間件,但是我們一般使用擴充方法來做這件事。

public static class MyMidlewareExtention
{
    public static IAppBuilder UseDing(this IAppBuilder app)
    {
        return app.Use<DingMiddleware>();
    }
}           

修改

Configuration

中的代碼:

public void Configuration(IAppBuilder app)
{
    app.UseDing();
    app.Run(HandleRequest);
}           

現在管道中有兩個中間件了,現在運作程式,在位址欄中輸入

http://localhost:8080/home/index

将得到如下結果

hello world from home/indexHello world from myOwin

因為Ding中間件在處理之後繼續調用了接下來的中間件

輸入其他路徑将得到

Hello world from myOwin

這個結果

如果将

Configuration

中的兩個中間件位置調換,的到的結果隻有一個

Hello world from myOwin

,因為

app.Run(HandleRequest);

不執行後續的中間件。

第二個中間件

public class DiDiDiMiddleware : OwinMiddleware
{
    public DiDiDiMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override Task Invoke(IOwinContext context)
    {
        if (context.Request.QueryString.Value == "boom")
        {
            context.Response.Write("Boom! Boom! Boom!");
            return Task.CompletedTask;
        }
        if (Next != null)
        {
            return Next.Invoke(context);
        }
        return Task.CompletedTask;
    }
}           

這個中間件在位址欄QueryString(?後邊的部分)等于boom時結束請求。

MyMidlewareExtention

代碼修改如下:

public static class MyMidlewareExtention
{
    public static IAppBuilder UseDing(this IAppBuilder app)
    {
        return app.Use<DingMiddleware>();
    }
    public static IAppBuilder UseDiDiDi(this IAppBuilder app)
    {
        return app.Use<DiDiDiMiddleware>();
    }
}           

Startup

類修改如下:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        app.UseDiDiDi();
        app.UseDing();
    }
}           

這裡去掉了

app.Run

此時,對于非

/home/index

的請求會得到404,是以我們暫時改動下代碼将

HandleRequest

方法封裝成一個預設的中間件

代碼改動如下:

public class DefaultMiddleware : OwinMiddleware
{
    public DefaultMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override Task Invoke(IOwinContext context)
    {
        var path = context.Request.Path.Value;
        context.Response.Write($"hey you come from {path}!");
        return Task.CompletedTask;
    }
}           
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseDiDiDi();
        app.UseDing();
        app.UseDefault();
    }
}           

運作程式觀察結果是否符合預期。

當位址中含有?boom時會的到一個Boom!Boom!Boom!

總結:Owin的管道概念其實簡單易懂,在程式啟動之前,向裡面插入中間件,中間件決定請求是否繼續向下走。在管道中的中間件可以拿到請求的所有資訊以對請求進行處理,管道裡的中間件執行結束之後,這個請求就被處理完成了,然後發回浏覽器。