Katana 專案入門

風靈使發表於2018-07-23

ASP.NET 首次在 2002 年釋出時,時代有所不同。 那時,Internet 仍處於起步階段,大約有 5.69 億使用者,每個使用者平均每天訪問 Internet 的時間為 46 分鐘,大約有 3 百萬個網站。 僅僅在 10 年之後,相同的測量指標揭示,大約有 22.7 億個 Internet 使用者,每個使用者平均每天訪問 Internet 的時間為 4 小時,大約有 5.55 億個網站(請參閱 bit.ly/MY7GzO)。

很顯然,這種增長相應地刺激了對應用程式開發者的需求變化,具體表現在生成和執行 Web 應用程式的基礎框架、工具和執行時等方面。 現代 Web 應用程式必須能夠快速地發展,充分利用許多不同的元件和框架。它們的佔用空間必須很小,這樣才能在雲的大型執行時環境中有效地執行。

確保 ASP.NET 能夠滿足這些當前需求和未來需求正是 Katana 專案的主要目標。

##Katana 簡介

Katana 專案實際可以追溯到 Microsoft 外部一個名為 Open Web Inter­face for .NET (OWIN) 的開放原始碼專案。OWIN 是一種定義 Web 伺服器和應用程式元件之間的互動的規範(請參閱 owin.org)。 由於這一規範的目的是發展一個廣闊且充滿活力的、基於 Microsoft .NET FrameworkWeb 伺服器和應用程式元件生態系統,因此它可以將伺服器與應用程式之間的互動減少到一小部分型別和單個函式簽名,這個函式簽名被稱為應用程式委託(即 AppFunc):

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

基於 OWIN 的應用程式中的每個元件都向伺服器提供應用程式委託。然後,這些元件連結成一個管道,基於 OWIN 的伺服器將會向該管道推送請求。為了更有效地使用資源,管道中的所有元件都應該是非同步的,這體現在返回 Task 物件的應用程式委託中。

包括應用程式狀態、請求狀態和伺服器狀態等在內的所有狀態都儲存在應用程式委託上指定的 IDictionary<string, object> 物件中。這種資料結構稱為環境字典,隨著請求通過管道時會從一個元件傳遞到另一個元件。雖然任何鍵/值資料都可以插入到環境字典中,但 OWIN 規範為某些 HTTP 核心元素定義了鍵,如圖 1 中所示。

Required Key Name Value Description
Yes owin.RequestBody A Stream with the request body, if any. Stream.Null MAY be used as a placeholder if there is no request body. See Request Body.
Yes owin.RequestHeaders An IDictionary<string, string[]> of request headers. See Headers.
Yes owin.RequestMethod A string containing the HTTP request method of the request (e.g., “GET”, “POST”).
Yes owin.RequestPath A string containing the request path. The path MUST be relative to the “root” of the application delegate. See Paths.
Yes owin.RequestPathBase A string containing the portion of the request path corresponding to the “root” of the application delegate; see Paths.
Yes owin.RequestProtocol A string containing the protocol name and version (e.g. “HTTP/1.0” or “HTTP/1.1”).
Yes owin.RequestQueryString A string containing the query string component of the HTTP request URI, without the leading “?” (e.g., “foo=bar&amp;baz=quux”). The value may be an empty string.
Yes owin.RequestScheme A string containing the URI scheme used for the request (e.g., “http”, “https”); see URI Scheme.

定義一組基本的環境字典鍵/值對,使得許多不同的框架和元件作者可以在一個 OWIN 管道中進行互操作,而不必強制實施對特定 .NET 物件模型的協議,例如針對 ASP.NET MVC 中的 HttpContextBaseASP.NET Web API 中的 HttpRequestMessage/HttpResponseMessage 的協議。

應用程式委託和環境字典這兩個元素構成了 OWIN 規範。Katana 專案是 Microsoft 建立和推出的基於 OWIN 的元件和框架集合。

Katana 元件可以通過體系結構堆疊檢視,如圖 2 中所示。

這裡寫圖片描述
圖 2 Katana 專案體系結構

此堆疊由以下層組成:

  • 主機:執行應用程式的程式,可以是從 IIS 或獨立可執行檔案到您自己的自定義程式的任何內容。主機負責啟動、載入其他 OWIN元件和正常關閉。
  • 伺服器:負責繫結到 TCP 埠,構造環境字典和通過 OWIN 管道處理請求。
  • 中介軟體:這是為處理 OWIN 管道中的請求的所有元件指定的名稱。它可以是從簡單壓縮元件到 ASP.NET Web API這樣的完整框架,不過從伺服器的角度而言,它只是一個公開應用程式委託的元件。
  • 應用程式:這是您的程式碼。由於 Katana 並不取代 ASP.NET,而是一種編寫和託管元件的新方式,因此現有的 ASP.NET Web APISignalR 應用程式將保持不變,因為這些框架可以參與 OWIN 管道。事實上,對於這些型別的應用程式,Katana 元件只需使用一個小的配置類即可見。

在體系結構方面,Katana 可以按其中每個層都能夠輕鬆替代的方式分解,通常不需要重新生成程式碼。在處理 HTTP 請求時,各個層一起工作,方式類似於圖 3 中所示的資料流。

這裡寫圖片描述
圖 3 Katana 中資料流的示例

##使用 Katana 生成現代 Web 應用程式

現代 Web 應用程式通常具有四項功能:

  1. 伺服器端標記生成
  2. 靜態檔案服務
  3. 用於處理 AJAX 請求的 Web API
  4. 實時訊息

使用所有這些功能生成應用程式時,需要專用於相關功能的各種不同框架。不過,從這些框架編寫應用程式頗具挑戰性,當前要求在 IIS 上託管不同的應用程式部分,可能需要使用應用程式和虛擬目錄將這些部分相互隔離。

與此相對的是,通過 Katana 可以利用各種不同的 Web 技術編寫現代 Web 應用程式,然後在所需的任何位置託管該應用程式,並且只在一個 HTTP 端點下公開。這樣可提供多種優勢:

  • 部署過程非常簡單,因為只涉及一個應用程式,而不是每種功能一個應用程式。
  • 可以新增驗證之類的額外功能,這些功能可應用於管道中的所有下游元件。
  • 不同的元件,無論是 Microsoft 還是第三方元件,都可以通過環境字典對同一個請求狀態執行操作。

現在,我將研究一個示例應用程式。該應用程式具有您應該熟悉的域:錯誤跟蹤。該應用程式將提供一組處於各種不同狀態(積壓、正在處理和已完成)的錯誤,並且我可以將錯誤在這些狀態之間變換。此外,由於許多人可能正在同時管理錯誤,當錯誤的狀態發生變化時,應用程式將會實時更新所有瀏覽器。下面是我將用於生成應用程式的內容:Nancy (nancyfx.org) 用於伺服器端標記生成和靜態檔案服務;ASP.NET Web API (asp.net/web-api) 用於處理 AJAX 請求;而 SignalR (signalr.net) 用於實時訊息服務。

另外,我不打算花費很多時間在瀏覽器客戶端的標記和指令碼上,我將使用 Knockout.js 來將 HTML 標記與 Web APISignalR 資料分隔開。

需要牢記的首要原則是,我正在將所有這些不同的框架編寫到一個 OWIN 管道中,這樣當新功能可用時,我只需將這些功能插入到管道,即可將它們新增到應用程式中。

##開始使用

Katana 的其中一個目標就是能夠讓您精確地控制新增到應用程式中的功能(這樣您就可以精確控制在每個請求的處理效能方面的工作)。牢記這一點後,我將從在 Visual Studio 2013 Preview 中建立一個空的新 ASP.NET Web 應用程式專案開始,如圖 4 中所示。

這裡寫圖片描述
圖 4 Visual Studio 2013 Preview 中的新 ASP.NET Web 應用程式專案

即使是空的 Web 專案模板也能提供有用的功能,因為在預設情況下,它們會將編譯的程式集直接放在 /bin 資料夾而不是 /bin/debug 資料夾中(這一點在其他專案型別中很常見)。預設的 Katana 主機會在此 /bin 資料夾中查詢程式集。您可以將基於 Katana 的應用程式建立為類庫,但您需要修改專案屬性以便符合此結構,或者提供自己的自定義應用程式載入程式,該載入程式能夠在其他資料夾結構中搜尋程式集和型別。

下一步,我將使用 Nancy Web 框架生成伺服器端標記生成程式碼。

Nancy 的語法非常簡潔,可以快速輕鬆地生成基於 HTTP 的站點和服務。本練習中更重要的事項是,與 ASP.NET Web API 類似,該應用程式與 System.Web.dll 沒有任何依賴關係,並且它的構建目的是為了在 OWIN 管道中執行。諸如 ASP.NET MVC 之類的框架與 System.Web.dll 具有依賴關係(截止編寫本文時),這使得它們不太適合用於非 IIS 託管方案。

大多數情況下,在您嚮應用程式新增新功能時,您將從新增 NuGet 程式包開始。(您可以在 docs.nuget.org 上詳細瞭解 NuGet。)在編寫本文時,此處使用的許多程式包都是預釋出版本,因此請確保可在 NuGet 對話方塊中顯示預釋出程式包。

為了將 Nancy 新增到應用程式中,我只需安裝 Nancy NuGet 程式包即可。不過,因為我還希望在 OWIN 管道中執行 Nancy,我打算安裝 Nancy.Owin 程式包 (nuget.org/packages/nancy.owin)。這會將 Nancy 程式包作為依賴關係安裝,並且提供額外的輔助方法在我的 OWIN 管道中配置 Nancy

下一步,我需要建立 Nancy 模組(類似於模型檢視控制器,或者稱 MVC、控制器)來處理請求,還需要建立檢視來向瀏覽器顯示內容。下面是模組 (HomeModule.cs) 的程式碼:

public class HomeModule : NancyModule
{
  public HomeModule() {
    Get["/"] = _ => {
      var model = new { title = "We've Got Issues..." };
      return View["home", model];
    };
  }
}

正如您看到的那樣,對於宣告定向到應用程式根 (“/”) 的請求的模組,應該由關聯 lambda 中定義的匿名委託來處理。 該函式會建立一個包含頁標題的模型,並指示 Nancy 呈現“主”檢視,並將模型傳遞到檢視。 如圖 5 中所示,該檢視會將模型的標題屬性同時插入到頁標題和 h1 元素中。

圖 5 Home.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>@Model.title</title>
</head>
  <body>
    <header>
      <h1>@Model.title</h1>   
    </header>
    <section>
      <h2>Backlog</h2>
      <ul class="bugs" id="backlog">
        <li>a bug</li>
      </ul>
    </section>
    <section>
      <h2>Working</h2>
      <ul class="bugs" id="working">
        <li>a bug</li>
      </ul>
    </section>
    <section>
      <h2>Done</h2>
      <ul class="bugs" id="done">
        <li>a bug</li>
      </ul>
    </section>
  </body>
</html>

有關這些列表的更多資訊,請參閱 Nancy 文件。

現在,我已經實現了基本的 Nancy 功能,我需要建立 OWIN 管道並配置 Nancy 模組來參與該管道。 為此,我首先需要安裝 Katana 主機和伺服器元件,然後編寫少量的探測程式碼來設定 OWIN 管道並將 Nancy 插入該管道中。

對於 Katana 主機和伺服器元件,我將從使用 IIS ExpressSystem.Web 入手,因為這些在本質上可以為 Visual Studio 所理解,並且將在生成應用程式時實現順暢的 F5 體驗。 為了將 System.Web 主機合併到專案中,我安裝了 NuGet 程式包 Microsoft.Owin.Host.SystemWeb

預設的 Katana 元件使用多個不同的約定來載入和執行 OWIN 應用程式,包括啟動類。 當 Katana 主機載入 OWIN 應用程式時,它會根據以下規則發現並執行啟動類(按照優先順序順序):

  • 果 web.config 檔案包含帶有 key=“owin:AppStartup”appSetting,則載入程式將使用設定值。該值必須是有效的 .NET 型別名稱。
  • 如果程式集包含屬性 [assembly:OwinStartup(typeof(MyStartup))],則載入程式將使用屬性值中指定的型別。
  • 如果這些條件中的任意一個為真,載入程式將通過查詢名為 Startup 的型別(帶有與簽名 void Configure(IAppBuilder app) 匹配的方法)來掃描已載入的程式集。

在本示例中,我將允許載入程式掃描該類的程式集。 不過,如果您的專案中有許多不同的型別和程式集,那麼使用 appSetting 或程式集屬性來防止不必要的掃描可能會更好。

我將建立啟動類,該類將初始化 OWIN 管道並將 Nancy 作為管道元件新增。 我建立一個名為 Startup 的新類,並按下面所示新增配置方法:

public class Startup
{
  public void Configuration(IAppBuilder app)
  {
    app.UseNancy();
  }
}

UseNancyNancy.Owin NuGet 程式包公開的擴充套件方法。 雖然您可以使用 IAppBuilder 的更通用的 Use 方法來新增中介軟體,不過許多中介軟體庫將提供這些有用的擴充套件方法,這些方法可以簡化配置過程。

此時,您可以在 Visual Studio 中使用 F5 執行專案,並且可以看到一個雖然不足以令人非常興奮,但功能完備的 Web 應用程式。 此時,OWIN 管道包含單個元件,即 Nancy,如圖 6 中所示。

這裡寫圖片描述
圖 6 包含單個元件的正常執行的 Web 應用程式

##將資料與 ASP.NET Web API 合併

目前,HTML 檢視由主靜態標記構成。 我現在將為使用者提供一些可處理的實際錯誤。 對於許多現代 Web 應用程式,將資料提供給瀏覽器客戶機的任務已從伺服器端標記生成框架(例如 Nancy 模組)轉移到單獨的 Web API 服務。 接下來,瀏覽器會載入 HTML 頁並立即執行 JavaScript,後者將從 Web API 中提取資料並在頁自身中動態生成 HTML 標記。

我將從使用 ASP.NET Web API 框架構造 Web API 開始入手。 通常,第一步是安裝 Web API NuGet 程式包。 為了確保輕鬆地將 ASP.NET Web API 插入到 OWIN 管道中,我將安裝 Microsoft.Asp­Net.WebApi.Owin 程式包 (bit.ly/1dnocmK)。 此程式包會將 ASP.NET Web API 框架的其餘部分作為依賴關係安裝。 在安裝框架後,我將建立一個簡單的 API,如圖 7 中所示。

圖 7 BugsController.cs

public class BugsController : ApiController
{
  IBugsRepository _bugsRepository = new BugsRepository();
  public IEnumerable<Bug> Get()
  {
    return _bugsRepository.GetBugs();
  }
  [HttpPost("api/bugs/backlog")]
  public Bug MoveToBacklog([FromBody] int id)
  {
    var bug = _bugsRepository.GetBugs().First(b=>b.id==id);
    bug.state = "backlog";
    return bug;
  }
  [HttpPost("api/bugs/working")]
  public Bug MoveToWorking([FromBody] int id)
  {
    var bug = _bugsRepository.GetBugs().First(b => b.id == id);
    bug.state = "working";
    return bug;
  }
  [HttpPost("api/bugs/done")]
  public Bug MoveToDone([FromBody] int id)
  {
    var bug = _bugsRepository.GetBugs().First(b => b.id == id);
    bug.state = "done";
    return bug;
  }
}

該 API 包含一個可從儲存庫返回一組錯誤物件的方法,還有一些可將錯誤在不同狀態之間變換的方法。 有關 ASP.NET Web API 的詳細資訊,請參閱 asp.net/web-api

現在我已定義了一個 ASP.NET Web API 控制器,我需要將它新增到現有的 OWIN 管道。 為此,我只需將下面的程式碼行新增到啟動類的配置方法中:

var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute("bugs", "api/{Controller}");
app.UseWebApi(config);

就如 Nancy 一樣,ASP.NET Web API OWIN 程式包提供了 UseWebApi 擴充套件方法,因此可以輕鬆地將 ASP.NET Web API 合併到現有的 OWIN 管道中。 現在,OWIN 管道包含兩個元件,即 ASP.NET Web APINancy,如圖 8 中所示。

這裡寫圖片描述
圖 8 包含兩個元件的 OWIN 管道

當請求進入管道中時,如果它們與其中一個 ASP.NET Web API 路由規則匹配,ASP.NET Web API 就會處理該請求並生成響應。 否則,請求會繼續通過管道,Nancy 會嘗試處理該請求。 如果沒有任何管道元件可以處理特定的請求,預設的 Katana 元件將返回 HTTP 404 響應。

儘管我已經有正常執行的 ASP.NET Web API,但它目前未由主檢視訪問。 因此,我將新增程式碼,以便從 Web API 使用資料,並且生成每個不同狀態的錯誤列表:積壓、正在處理和已完成。 對於此任務,我將利用 Knockout.js,這是一個 JavaScript 的“模型-檢視-檢視模型”(MVVM) 庫。 有關 Knockout 的更多資訊,請參閱 knockoutjs.com

為了使用 Knockout 啟用在客戶端動態生成 HTML 標記的功能,我首先需要做的工作是從 ASP.NET Web API 中提取所有錯誤,並建立 Knockout 可繫結到 HTML 元素的 viewModel。 如圖 9 所示。

圖 9 設定錯誤 viewModel

<script>
  $(function () {
    var viewModel;
    $.getJSON('/api/bugs', function(data) {
      var model = data;
      viewModel = {
        backlog: ko.observableArray(
          model.filter(function(element) { return element.state === 'backlog'; })),
        working: ko.observableArray(
          model.filter(function(element) { return element.state === 'working'; })),
        done: ko.observableArray(
          model.filter(function(element) { return element.state === 'done'; })),
        changeState: function (bug, newState) {
          var self = this;
          $.post('/api/bugs/' + newState, { '': bug.id }, function(data){
            self.moveBug(data);
          });
        },
        moveBug: function (bug) {
          // Remove the item from one of the existing lists
          ...
          // Add bug to correct list
          this[bug.state].push(bug);
        }
      };
      ko.applyBindings(viewModel);
    })
  })
</script>

在建立 viewModel 後,Knockout 接下來可以通過將 viewModel 繫結到使用 Knockout 特定屬性修飾的 HTML 元素,動態地生成並更新 HTML 內容。 例如,積壓列表可以從 viewModel 使用圖 10 中所示的屬性生成。

圖 10 用於生成積壓列表的屬性

<section>
  <h2>Backlog</h2>
  <ul class="bugs" id="backlog" data-bind="foreach:backlog">
    <li>
      [<span data-bind="text: id"></span>] <span data-bind="text: title"></span>:
        <span data-bind="text: description"></span>
      <ul>
        <li><a href="#" data-bind="click: $root.changeState.bind($root, $data,
          'working')">Move to working</a></li>   
        <li><a href="#" data-bind="click: $root.changeState.bind($root, $data,
          'done')">Move to done</a></li>   
      </ul>
    </li>
  </ul>
</section>

##使用 SignalR 新增實時更改通知

此時,我已經有了一個功能完備的單頁 Web 應用程式。 使用者可以瀏覽到主檢視,並將錯誤在不同的錯誤狀態之間變換。 此外,當前功能級別的基礎技術 NancyASP.NET Web API 在相同的 OWIN 管道中一起執行。

不過,我打算更深入一步,允許不同的使用者實時檢視其他使用者對錯誤所做的更新。 為此,我將利用 SignalR 庫。該庫提供了客戶機和伺服器 API 來管理瀏覽器與 Web 伺服器之間的實時訊息交換。 編寫 SignalR 的目的也是為了在 OWIN 管道中執行,因此將它新增到現有的應用程式中不過是小事一樁。

我將使用一項名為 HubSignalR 功能,但在本文中不會討論有關 SignalR 的細節。Hub 使客戶端和伺服器能夠相互呼叫對方的方法。在我的應用程式中,當 ASP.NET Web API 接收到更改錯誤狀態的請求時,它將會更新錯誤,然後通過 SignalR Hub 將更新後的錯誤廣播給當前連線到應用程式的所有瀏覽器客戶端。

我將從在伺服器上建立 Hub 來開始此過程。 因為我不會利用任何其他 SignalR 功能,因此我的 Hub 將只包含以下空的類定義:

[HubName("bugs")]
public class BugHub : Hub
{
}

為了將廣播從 ASP.NET Web API 傳送到 Hub,我首先需要獲取器執行時上下文的例項。 我可以通過新增以下 BugsController 建構函式來實現此目的:

public BugsController()
{
  _hub = GlobalHost.ConnectionManager.GetHubContext<BugHub>();
}

從其中一個 MoveToXX 操作,我接下來可以將更新的錯誤廣播到所有連線的瀏覽器客戶端:

_hub.Clients.All.moved(bug);

在主檢視中,將幾個指令碼引用新增到 SignalR JavaScript 庫後,我可以使用以下程式碼連線到 bugsHub 並開始偵聽“已變換”訊息:

$.connection.hub.logging = true;
var bugsHub = $.connection.bugs;
bugsHub.client.moved = function (item) {
  viewModel.moveBug(item);
};
$.connection.hub.start().done(function() {
  console.log('hub connection open');
});

請注意,當我通過 moved 函式從伺服器接收到呼叫時,我按照該項的單擊處理程式的相同方式呼叫 viewModel moveBug 方法。 差別在於,由於此方法是 SignalR 廣播的結果,因此所有瀏覽器客戶端可以同時更新其 viewModel。 通過開啟兩個瀏覽器視窗,在其中一個視窗中進行更改,然後在另一個視窗中檢視狀態更改,即可清楚地瞭解這一點。

正如我所提到的,將 SignalR 新增到 OWIN 管道中不過是小事一樁。 我只需將下面的程式碼新增到啟動類的配置方法中:

app.MapSignalR();

這將建立類似圖 11 中所示的管道。

這裡寫圖片描述
圖 11 包含三個元件的 OWIN 管道

##轉向自託管

我現在有了一個正常執行的錯誤管理應用程式,但仍缺少一些關鍵功能來執行某些有意思的操作。 我已經使用 Microsoft 和第三方 Katana 中介軟體元件,逐步向應用程式新增了一些功能。 不過,今天許多此類功能已可以使用 ASP.NET HttpModulesHttpHandlers 來實現。 因此,除了提供更加簡單的、程式碼驅動的方法來編寫管道元件之外,我真正實現了哪些功能?

答案就是記住圖 2 中的高階 Katana 體系結構圖表。 到目前為止,我只使用了 Katana 堆疊的最上面兩層。 不過,所有這些層都可以輕鬆地替換,包括伺服器和主機。

為了進行演示,我將處理我的整個管道,將它提升到 IISSystem.Web.dll 之外,並將它放在一個簡單的輕型 HTTP 伺服器上,該伺服器是由名為 OwinHost.exeKatana 可執行檔案託管的。 自託管在許多方案中是非常有用的,從開發計算機上未安裝 Web 伺服器的安裝情形,到在使用程式隔離並且不公開 Web 伺服器訪問的共享託管環境中部署應用程式的生產情形。

我將從安裝以下附加 NuGet 程式包開始:

  • Microsoft.Owin.Host.HttpListener
  • OwinHost

然後,我將重新生成應用程式。 請注意,對於在新伺服器和主機之上執行應用程式,重新生成操作並不是必需的。 唯一的要求是,在執行時,/bin 資料夾中必須存在這些檔案,而重新生成是一種將檔案複製到 /bin 的便捷方式。

在安裝程式包和複製檔案之後,我開啟命令提示符,導航到 Web 專案的根資料夾,然後從程式包資料夾中呼叫 OwinHost.exe,如圖 12 中所示:

…\packages\OwinHost.2.0.0\tools\OwinHost.exe

這裡寫圖片描述
圖 12 從程式包資料夾中呼叫 OwinHost.exe

預設情況下,OwinHost.exe 將啟動,載入 Microsoft.Ow­in.Host.HttpListener 伺服器,然後開始偵聽埠 5000。 接下來,我可以導航到 http://localhost:5000,確認整個應用程式正在執行。

此外,幾乎所有的預設設定都可以使用命令列開關進行覆蓋。 例如,如果您希望偵聽其他埠,可以提供 -p 12345。 如果希望使用完全不同的伺服器,請使用 -s your.custom.server.assemblyKatana 設計的強大之處在於它的模組化。 當堆疊中的任何層發生創新後,它們可以立即整合到正在執行的應用程式。 並且,由於堆疊的所有元件之間的約定只是應用程式委託,創新的步伐可以遠遠超越目前提供的功能。
這僅僅是一個開端

Katana 2.0 將隨 Visual Studio 2013 一起釋出。 新版本有兩個值得關注的方面:

  • 為自託管提供核心基礎結構元件
  • 提供了一套豐富的驗證中介軟體(包括 Facebook、Google、Twitter 和 Microsoft Account 這樣的社交提供商)以及適用於 Windows Azure Active Directorycookie 和聯合身份驗證的提供程式

在釋出 Katana 2.0 後,將會立即開始未來的 Katana 元件集的開發工作。 具體細節和優先順序仍有待確定,不過您可以在 katanaproject.codeplex.com 中建立問題,推動這種討論。

相關文章