Asp.Net MVC<九>:OWIN,關於StartUp.cs

https://msdn.microsoft.com/zh-cn/magazine/dn451439.aspx (Katana 項目入門)html

一不當心寫了個WEB服務器git

快刀斬亂麻之 Katanagithub

OWIN知識

OWIN的全稱是Open Web Interface For .Net。web

OWIN提供的只是一種規範,而沒有具體實現。其目的是在web服務器和應用程序組件之間隔離出一個抽象層,使它們之間解耦。json

應用程序委託和環境字典

OWIN將服務器與應用程序之間的交互減小到一小部分類型和單個函數簽名,這個函數簽名被稱爲應用程序委託(即 AppFunc):api

using AppFunc = Func<IDictionary<string, object>, Task>;

基於 OWIN 的應用程序中的每一個組件都向服務器提供應用程序委託。 而後,這些組件連接成一個管道,基於 OWIN 的服務器將會向該管道推送請求。 服務器

 

一個符合OWIN的web服務器,須要將請求信息(應用程序狀態、請求狀態和服務器狀態等全部相關信息)包裝到一個字典裏(應用程序委託上指定的 IDictionary<string, object>,這種數據結構稱爲環境字典),從而使得許多不一樣的框架和組件做者能夠在一個 OWIN 管道中進行互操做,而沒必要強制實施對特定 .NET 對象模型的協議。數據結構

雖然任何鍵/值數據均可以插入到環境字典中,但 OWIN 規範爲某些 HTTP 核心元素定義了鍵: 架構

key value
"owin.RequestBody" 一個帶有請求正文(若是有)的流。若是沒有請求正文,Stream.Null 能夠用做佔位符。
"owin.RequestHeaders" 請求標頭的 IDictionary<string, string[]>。
"owin.RequestMethod" 一個包含請求的 HTTP 請求方法的字符串(例如 GET 和 POST)。
"owin.RequestPath" 一個包含請求路徑的字符串。 此路徑必須是應用程序委託的「根」的相對路徑。
"owin.RequestPathBase" 一個字符串,包含對應於應用程序委託的「根」的請求路徑部分。
"owin.RequestProtocol" 一個包含協議名稱和版本的字符串(例如 HTTP/1.0 或 HTTP/1.1)。
"owin.RequestQueryString" 一個字符串,包含 HTTP 請求 URI 的查詢字符串組成部分,不帶前導「?」(例如 foo=bar&baz=quux)。 該值能夠是空字符串。
"owin.RequestScheme" 一個字符串,包含用於請求的 URI 方案(例如 HTTP 或 HTTPS)。

隨着請求在OWIN管道中流動,每一箇中間件(Middleware,集成到管道中的組件或應用程序)所要作的就是讀取、修改這個字典的數據。最後,Web服務器獲得這個層層處理過的字典,而後輸出網頁到客戶端。app

 

與ASP.NET管道相比,OWIN規範很是簡潔,且並無引用.Net Framework中的System.Web.dll。

   1. 新的組件可以很是簡單的開發和應用
   2. 程序可以簡便地在host和OS上遷移

Katana

Katana 項目是 Microsoft 建立和推出的基於 OWIN 的組件和框架集合。

https://katanaproject.codeplex.com

但上面這個項目最後一次提交是15年1月

目前它的託管代碼已經被拆分:

New 'Katana' Source:

Katana 體系結構

 

  • 主機:運行應用程序的進程,能夠是從 IIS 或獨立可執行文件到您本身的自定義程序的任何內容。 主機負責啓動、加載其餘 OWIN 組件和正常關閉。
  • 服務器:負責綁定到 TCP 端口,構造環境字典和經過 OWIN 管道處理請求。
  • 中間件:這是爲處理 OWIN 管道中的請求的全部組件指定的名稱。 它能夠是從簡單壓縮組件到 ASP.NET Web API 這樣的完整框架,不過從服務器的角度而言,它只是一個公開應用程序委託的組件。
  • 應用程序:這是您的代碼。 因爲 Katana 並不取代 ASP.NET,而是一種編寫和託管組件的新方式,所以現有的 ASP.NET Web API 和 SignalR 應用程序將保持不變,由於這些框架能夠參與 OWIN 管道。 事實上,對於這些類型的應用程序,Katana 組件只需使用一個小的配置類便可見。

在體系結構方面,Katana 能夠按其中每一個層都可以輕鬆替代的方式分解,一般不須要從新生成代碼。 在處理 HTTP 請求時,各個層一塊兒工做,方式相似於下圖中所示的數據流。

————

使用IIS作Host和Server

首先創建一個空的web應用程序。由於默認的 Katana 主機會在此 /bin 文件夾中查找程序集。而Web應用程序默認會將編譯的程序集直接放在 /bin 文件夾而不是 /bin/debug 文件夾中。

刪除無關文件,引入OWIN的支持包

添加Startup啓動類

Startup類的做用是用來初始化OWIN管道,這裏,咱們添加和初始化OWIN管道中的Middleware.

 在Startup.Configuration方法中,添加以下代碼:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // New code:
        app.Run(context =>
        {
            context.Response.ContentType = "text/plain";
            return context.Response.WriteAsync("Hello, world."+ context.Request.Uri);
        });
    }
}

上面的代碼作的事情,就是把一個簡單的Middleware註冊到OWIN管道中。

其中context的類型是IOwinContext:

public interface IOwinContext
{
    //     Gets the Authentication middleware functionality available on the current request.
    IAuthenticationManager Authentication { get; }

    //     Gets the OWIN environment.
    IDictionary<string, object> Environment { get; }

    //     Gets a wrapper exposing request specific properties.
    IOwinRequest Request { get; }

    //     Gets a wrapper exposing response specific properties.
    IOwinResponse Response { get; }

    //     Gets or sets the host.TraceOutput environment value.
    TextWriter TraceOutput { get; set; }

    //     Gets a value from the OWIN environment, or returns default(T) if not present.
    T Get<T>(string key);

    //     Sets the given key and value in the OWIN environment.
    IOwinContext Set<T>(string key, T value);
}

 運行結果:

 如圖 能夠順利解析到不一樣的訪問Url,天然也就能夠在後續的處理中作出不一樣的處理,直接分支處理或讀取靜態文件或者實現MVC架構等等……。

改用其餘形式的Host和Server

上例中IIS同時充當了Host和Server的角色, 

首先建立一個簡單的Console應用程序,用Nuget添加Microsoft.Owin.SelfHost

 

以一樣的方式添加Startup啓動類

將控制檯程序改造爲Host

class Program
{
    static void Main(string[] args)
    {
        using (Microsoft.Owin.Hosting.WebApp.Start<Startup1>("http://localhost:9000"))
        {
            Console.WriteLine("Press [enter] to quit...");
            Console.ReadLine();
        }
    }
}

運行結果:

Startup類

不管使用IIS, IIS Express仍是OWIN Host, 微軟在這些Host上實現的Service都會依照特定的規則來尋找到Startup類,執行Configuration方法,註冊Middleware。

默認名稱匹配
能夠定義Startup.cs類,只要這個類的namespace和Assembly的名稱相同。那麼,這個Startup.cs中的Configuration方法,就會在OWIN管道初始化的時候執行。

使用OwinStartup Attribute
直接指定哪一個具體類是Startup類。

在配置文件的appSetting 節點設置

<appSettings>  
  <add key="owin:appStartup" value="StartupDemo.ProductionStartup" />
</appSettings>

路由配置

protected void Application_Start(object sender, EventArgs e)
{
     // Registers a route for the default OWIN application.
     RouteTable.Routes.MapOwinPath("/owin");
	 // Invokes the System.Action startup delegate to build the OWIN application and
     // then registers a route for it on the given path.
     RouteTable.Routes.MapOwinPath("/special", app =>
     {
         app.Run(OwinApp2.Invoke);
     });
} 
public class OwinApp2
{
    // Invoked once per request.
    public static Task Invoke(IOwinContext context)
    {
        context.Response.ContentType = "text/plain";
        return context.Response.WriteAsync("Hello World 2");
    }
}

自定義Middleware

經過繼承OwinMiddleware基類能夠便捷地新建中間件:

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

   public override Task Invoke(IOwinContext context)
   {
       var response = "Hello World! It is " + DateTime.Now;
       context.Response.Write(response);
       return Next.Invoke(context);
   }
}

註冊:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use<HelloWorldMiddleware>();
    }
} 

 

應用

ASP.NET Web Form,ASP.NET MVC5項目結合OWIN

因爲ASP.NET Web Form和ASP.NET MVC5依賴於System.Web.dll中的不少類型,而在OWIN管道中,是沒法提供這些依賴的。因此ASP.NET Web Form和ASP.NET MVC5不能做爲一箇中間件直接集成到OWIN管道中。

但在這些項目中,也能夠添加Startup.cs, 指定成爲OWIN的初始化類型,那麼請求會先通過OWIN管道處理,最後轉向ASP.NET Web Form或者ASP.NET MVC程序。這種方式,經常用來配置log, authentication, cache等等這些Middleware。

引入OWIN後的管道執行順序

Web API做爲Middleware註冊到OWIN管道中

Web API因爲無任何依賴於System.web.dll, 因此能夠做爲Middleware註冊到OWIN管道中。

public class Startup
{
    // Invoked once at startup to configure your application.
    public void Configuration(IAppBuilder builder)
    {
        HttpConfiguration config = new HttpConfiguration();
        config.Routes.MapHttpRoute("Default", "api/{controller}/{customerID}", new { controller = "Customer", customerID = RouteParameter.Optional });//定義web api route
        //xml格式輸出結果 
        config.Formatters.XmlFormatter.UseXmlSerializer = true;

        config.Formatters.Remove(config.Formatters.JsonFormatter);
        // config.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;
        //將web api以Middleware註冊到OWIN管道中
        builder.UseWebApi(config);
    }
}

ASP.NET 5

ASP.NET 5中終於去System.web.dll化,MVC,Web API都統一在了OWIN管道中。

ASP.NET 5 的 project.json 配置文件(Microsoft.Owin改作Microsoft.AspNet 了):

ASP.NET 5 中Startup.cs 的兩個重要方法:

// This method gets called by the runtime.
public void ConfigureServices(IServiceCollection services)
{
    // Add EF services to the services container.
    services.AddEntityFramework(Configuration)
        .AddSqlServer()
        .AddDbContext<ApplicationDbContext>();

    // Add Identity services to the services container.
    services.AddDefaultIdentity<ApplicationDbContext, ApplicationUser, IdentityRole>(Configuration);

    // Add MVC services to the services container.
    services.AddMvc();

    // Uncomment the following line to add Web API servcies which makes it easier to port Web API 2 controllers.
    // You need to add Microsoft.AspNet.Mvc.WebApiCompatShim package to project.json
    // services.AddWebApiConventions();
}

// Configure is called after ConfigureServices is called.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
    // Configure the HTTP request pipeline.
    // Add the console logger.
    loggerfactory.AddConsole();

    // Add the following to the request pipeline only in development environment.
    if (string.Equals(env.EnvironmentName, "Development", StringComparison.OrdinalIgnoreCase))
    {
        app.UseBrowserLink();
        app.UseErrorPage(ErrorPageOptions.ShowAll);
        app.UseDatabaseErrorPage(DatabaseErrorPageOptions.ShowAll);
    }
    else
    {
        // Add Error handling middleware which catches all application specific errors and
        // send the request to the following path or controller action.
        app.UseErrorHandler("/Home/Error");
    }

    // Add static files to the request pipeline.
    app.UseStaticFiles();

    // Add MVC to the request pipeline.
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller}/{action}/{id?}",
            defaults: new { controller = "Home", action = "Index" });

        // Uncomment the following line to add a route for porting Web API 2 controllers.
        // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}");
    });
}

  ConfigureServices 在運行時的時候被運行,Configure 運行在 ConfigureServices 以後,查看 ConfigureServices 中的 Add 方法註釋,你會發現最後一個單詞老是 container(容器),這是怎麼回事呢,其實就是往Ioc容器中注入類型依賴的對象,這些類型對象的管理都是在 Owin 管道中的,你只須要在 ConfigureServices 中使用 Add 方法註冊相應模塊就能夠了,其餘的東西 ASP.NET 5 會幫你完成,而 Configure 是什麼做用呢?我本身以爲它是配置模塊的一個「配置」,用戶你使用中間件或者應用程序的一個配置,好比,你使用 app.UseCookieAuthentication 進行配置用戶驗證的一些操做,你查看 UseCookieAuthentication 的定義,會發現其命名空間爲 Microsoft.AspNet.Builder.CookieAuthenticationExtensions,所在程序集爲 CookieAuthenticationExtensions(Owin 中間件),查看 Configure 中其餘 Use 使用,你一樣會發現命名空間都是 Microsoft.AspNet.Builder 開頭,以前說 Owin 是一種協定,Extensions 就是一種中間件和應用程序的擴展,但都必須符合此協定,這樣纔會有無限可能。

相關文章
相關標籤/搜索