在上一篇中簡單講了一些基礎知識,例如Asp.Net Core Middleware 的使用,DI的簡單使用以及嵌入式資源的使用方法等。本篇就是結合基礎知識來構建一個基礎框架出來。git
那麼框架有什麼功能呢?github
下面就基於以上四點搭建基礎框架。其餘緩存,日誌什麼的就先不在介紹。緩存
正如上一篇介紹的那樣,實現一箇中間件就能夠作攔截請求操做,換句話說,若是是layim的請求,咱們不要放過。若是不是,那麼拜拜。可是因爲咱們又使用了系統的 EmbeddedFileProvider ,因此靜態資源交給系統去處理就好。這裏呢我使用一個很簡單的方式來判斷是不是LayIM的請求,就是經過請求的path前綴去判斷。在 LayIMMiddleware入口方法Invoke中,經過IsLayIMRequest擴展方法去判斷是不是LayIM請求。代碼以下:app
/// <summary> /// 是否LayIM接口請求 /// </summary> /// <param name="context"></param> /// <param name="options"></param> /// <returns></returns> public static bool IsLayIMRequest(this HttpContext context, LayIMOptions options) { return IsConfigPath(context.Request.Path.Value) || context.Request.Path.Value.StartsWith(options.ApiPrefix, StringComparison.CurrentCultureIgnoreCase); }
沒錯,就這麼簡單粗暴,用了一個StartWith方法。代碼中IsConfigPath之後在講。在這裏,前綴能夠是用戶自定義的。能夠在UselayIM中傳入定義方法:框架
app.UseLayIM(options => { options.ApiPrefix = "/mylayim"; });
好比上文中我改爲了/mylayim開頭的,測試一下。ide
能夠看到,正常處理。函數
正如上文中的路徑 /mylayim/init?uid=1 是如何進行處理的呢?這裏咱們的路由就要出場了。以前這段代碼仍是借鑑了Hangfire中 的代碼實現的。它的路由很簡單,就是經過正則去匹配。不過我這裏實現的路由沒有那麼強大,爲了方便,不少url都定義死了。並且不支持url中帶參數解析的狀況,例如 init/{uid}.不過這個後期會考慮。路由匹配代碼以下:測試
/// <summary> /// 經過path找到對應的Dispatcher /// </summary> /// <param name="path"></param> /// <returns></returns> private Tuple<ILayIMDispatcher, Match> FindDispatcherMatch(string path) { if (string.IsNullOrEmpty(path)) { path = "/"; } foreach (var dispatcher in dispatchers) { var pattern = $"^{dispatcher.Item1}$" ; var match = Regex.Match(path, pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Singleline); if (match.Success) { return new Tuple<ILayIMDispatcher, Match>(dispatcher.Item2, match); } } return null; }
沒錯就這麼一個方法實現了路由,是否是很簡單。(複雜的還沒去研究。。。。)從代碼中咱們能夠看到方法返回了一個 Tuple<ILayIMDispatcher, Match> ,這個ILayIMDispatcher是何方神聖呢?讓咱們進入下一節吧。ui
這個調度器翻譯的不是很準確,不過你們理解就好。若是不理解的話,看下面的圖就知道了。this
接口ILayIMDispatcher裏面就一個方法
Task Dispatch(HttpContext context);
那麼他們又能夠細分爲多種類型,在CQRS的概念裏,咱們對聚合的增刪改都屬於命令(Command),那麼咱們能夠定義一個CommandDispatcher,不過我這裏沒有那麼嚴格按照CQRS的方式,因此查詢我也把他歸類爲查詢命令:QueryCommandDispatcher。
下面咱們看一下具體代碼:
internal class QueryCommandDispatcher<TResult> : CommandDispatcher<TResult> { protected override string AllowMethod => HttpGet; private readonly Func<HttpContext, TResult> executeFunction; public QueryCommandDispatcher(Func<HttpContext, TResult> executeFunction) { this.executeFunction = executeFunction; } }
在構造函數裏面咱們傳入了一個 Func<HttpContext, TResult>,那麼這個Func就是咱們的業務邏輯了。
好比在路由裏,咱們添加 /layim/init 的 QueryCommandDispatcher。代碼以下:
//layim初始化接口 routes.AddQueryCommand<object>("/init", context => { //這裏只是演示(邏輯未實現) return context.Request.Query["uid"]; });
其中AddQueryCommand是路由的一個擴展方法:
/// <summary> /// 註冊返回值爲TResult類型的命令路由 /// </summary> /// <typeparam name="TResult">返回類型</typeparam> /// <param name="routes">當前路有集合</param> /// <param name="path">路徑</param> /// <param name="command">執行命令</param> public static void AddQueryCommand<TResult>(this RoutesCollection routes, string path, Func<HttpContext, TResult> command) { Error.ThrowIfNull(path, nameof(path)); Error.ThrowIfNull(command, nameof(command)); routes.Add(path, new QueryCommandDispatcher<TResult>(command)); }
那麼,這樣的話,路由第一步先找到相對應 /layim/init 的調度器,而後執行Dispatch方法便可,最後返回所須要的數據。正如第一節裏的截圖那個最終處理效果。
通用接口其實在以前的文章中有講過,他的做用就是業務和框架解耦。也就是說我設計好一個通用接口,若是用戶不想使用框架的默認實現,能夠自行定義實現方法,而後經過依賴注入的形式替換掉框架默認實現,這裏不在贅述。好比框架的默認實現是Dapper,那麼用戶能夠本身改成EntityFramework或者其餘實現。
本文簡單的介紹了框架的結構和基本實現,實現較爲簡單,功能相對來講比較單一,不過因爲是偏向LayIM業務的,因此並無想把它設計的多麼複雜,功能多麼強大,並且主要是能力不夠,哈哈哈哈。
博客預告:LayIM.AspNetCore Middleware 開發日記(四)主角登場(LayIM介紹)
項目地址:https://github.com/fanpan26/LayIM.AspNetCore (本文代碼對應blog3分支或者直接查看master)歡迎小夥伴們star 圍觀 提意見。