Katana 專案入門
當 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 Interface for .NET (OWIN)
的開放原始碼專案。OWIN
是一種定義 Web
伺服器和應用程式元件之間的互動的規範(請參閱 owin.org)。 由於這一規範的目的是發展一個廣闊且充滿活力的、基於 Microsoft .NET Framework
的 Web
伺服器和應用程式元件生態系統,因此它可以將伺服器與應用程式之間的互動減少到一小部分型別和單個函式簽名,這個函式簽名被稱為應用程式委託(即 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&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
中的 HttpContextBase
或 ASP.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 API
和SignalR
應用程式將保持不變,因為這些框架可以參與OWIN
管道。事實上,對於這些型別的應用程式,Katana
元件只需使用一個小的配置類即可見。
在體系結構方面,Katana
可以按其中每個層都能夠輕鬆替代的方式分解,通常不需要重新生成程式碼。在處理 HTTP
請求時,各個層一起工作,方式類似於圖 3 中所示的資料流。
圖 3 Katana 中資料流的示例
##使用 Katana
生成現代 Web
應用程式
現代 Web 應用程式通常具有四項功能:
- 伺服器端標記生成
- 靜態檔案服務
- 用於處理 AJAX 請求的 Web API
- 實時訊息
使用所有這些功能生成應用程式時,需要專用於相關功能的各種不同框架。不過,從這些框架編寫應用程式頗具挑戰性,當前要求在 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 API
及 SignalR
資料分隔開。
需要牢記的首要原則是,我正在將所有這些不同的框架編寫到一個 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 Express
和 System.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();
}
}
UseNancy
是 Nancy.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.AspNet.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 API
和 Nancy
,如圖 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 應用程式。 使用者可以瀏覽到主檢視,並將錯誤在不同的錯誤狀態之間變換。 此外,當前功能級別的基礎技術 Nancy
和 ASP.NET Web API
在相同的 OWIN
管道中一起執行。
不過,我打算更深入一步,允許不同的使用者實時檢視其他使用者對錯誤所做的更新。 為此,我將利用 SignalR
庫。該庫提供了客戶機和伺服器 API 來管理瀏覽器與 Web
伺服器之間的實時訊息交換。 編寫 SignalR 的目的也是為了在 OWIN
管道中執行,因此將它新增到現有的應用程式中不過是小事一樁。
我將使用一項名為 Hub
的 SignalR
功能,但在本文中不會討論有關 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 HttpModules
和 HttpHandlers
來實現。 因此,除了提供更加簡單的、程式碼驅動的方法來編寫管道元件之外,我真正實現了哪些功能?
答案就是記住圖 2 中的高階 Katana
體系結構圖表。 到目前為止,我只使用了 Katana
堆疊的最上面兩層。 不過,所有這些層都可以輕鬆地替換,包括伺服器和主機。
為了進行演示,我將處理我的整個管道,將它提升到 IIS
和 System.Web.dll
之外,並將它放在一個簡單的輕型 HTTP
伺服器上,該伺服器是由名為 OwinHost.exe
的 Katana
可執行檔案託管的。 自託管在許多方案中是非常有用的,從開發計算機上未安裝 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.Owin.Host.HttpListener
伺服器,然後開始偵聽埠 5000
。 接下來,我可以導航到 http://localhost:5000,確認整個應用程式正在執行。
此外,幾乎所有的預設設定都可以使用命令列開關進行覆蓋。 例如,如果您希望偵聽其他埠,可以提供 -p 12345
。 如果希望使用完全不同的伺服器,請使用 -s your.custom.server.assembly
。 Katana
設計的強大之處在於它的模組化。 當堆疊中的任何層發生創新後,它們可以立即整合到正在執行的應用程式。 並且,由於堆疊的所有元件之間的約定只是應用程式委託,創新的步伐可以遠遠超越目前提供的功能。
這僅僅是一個開端
Katana 2.0
將隨 Visual Studio 2013
一起釋出。 新版本有兩個值得關注的方面:
- 為自託管提供核心基礎結構元件
- 提供了一套豐富的驗證中介軟體(包括 Facebook、Google、Twitter 和 Microsoft Account 這樣的社交提供商)以及適用於
Windows Azure Active Directory
、cookie
和聯合身份驗證的提供程式
在釋出 Katana 2.0
後,將會立即開始未來的 Katana
元件集的開發工作。 具體細節和優先順序仍有待確定,不過您可以在 katanaproject.codeplex.com
中建立問題,推動這種討論。
相關文章
- Kotlin專案入門Kotlin
- SpringBoot入門 - 建立專案Spring Boot
- maven 專案的建立入門Maven
- Vue專案入門例項Vue
- Spring入門(一):建立Spring專案Spring
- SpringBoot + Mybatis + Redis 整合入門專案Spring BootMyBatisRedis
- springboot+dubbo+nacos入門專案Spring Boot
- Django-APP及專案入門DjangoAPP
- SAP Commerce Cloud 專案 Spartacus 入門Cloud
- 前端專案docker:前端docker入門前端Docker
- GraphQL搭配MongoDB入門專案實戰MongoDB
- scrapy入門教程()部署爬蟲專案爬蟲
- Gradle入門及SpringBoot專案構建GradleSpring Boot
- go+vue入門專案練手GoVue
- 上手一個 Vue 的入門專案Vue
- Spring Boot入門-快速搭建web專案Spring BootWeb
- SpringCloud入門及建立分散式專案SpringGCCloud分散式
- Vue入門到關門之Vue3專案建立Vue
- Vue入門到關門之Vue專案工程化Vue
- 從一個小專案快速入門ScssCSS
- 華為開源專案ServiceComb快速入門
- Scrapy入門-第一個爬蟲專案爬蟲
- Java爬蟲入門(一)——專案介紹Java爬蟲
- `.NET Web`新人入門必學專案`EarthChat`Web
- Spring Boot入門(一):搭建Spring Boot專案Spring Boot
- Spring入門(四):使用Maven管理Spring專案SpringMaven
- OpenGL入門(1)——建立一個OpenGL專案
- 【CuteJavaScript】Angular6入門專案(1.構建專案和建立路由)JavaScriptAngular路由
- Angularjs 從入門到實戰(含專案教程)AngularJS
- Spring boot入門(一):快速搭建Spring boot專案Spring Boot
- IntelliJ IDEA配置與搭建web專案入門使用IntelliJIdeaWeb
- Mybatis入門——基礎操作(基於springboot專案)MyBatisSpring Boot
- web專案技術必備-------jQuery快速入門WebjQuery
- 專案之爬蟲入門(豆瓣TOP250)爬蟲
- Kubernetes 入門實踐--部署執行 Go 專案Go
- 大資料入門指南(GitHub開源專案)大資料Github
- React-Native入門(1)-專案工程初識React
- vue 快速入門 系列 —— Vue(自身) 專案結構Vue