LayIM.AspNetCore Middleware 開發日記(三)基礎框架搭建

丶Pz發表於2018-05-30

前言  

  在上一篇中簡單講了一些基礎知識,例如Asp.Net Core Middleware 的使用,DI的簡單使用以及嵌入式資源的使用方法等。本篇就是結合基礎知識來構建一個基礎框架出來。

  那麼框架有什麼功能呢?

  1. 攔截LayIM請求
  2. 簡單路由功能
  3. 路由排程器
  4. 通用介面

  下面就基於以上四點搭建基礎框架。其他快取,日誌什麼的就先不在介紹。

攔截LayIM請求

  正如上一篇介紹的那樣,實現一箇中介軟體就可以做攔截請求操作,換句話說,如果是layim的請求,我們不要放過。如果不是,那麼拜拜。但是由於我們又使用了系統的 EmbeddedFileProvider ,所以靜態資源交給系統去處理就好。這裡呢我使用一個很簡單的方式來判斷是否是LayIM的請求,就是通過請求的path字首去判斷。在 LayIMMiddleware入口方法Invoke中,通過IsLayIMRequest擴充套件方法去判斷是否是LayIM請求。程式碼如下:

     /// <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開頭的,測試一下。

  

  可以看到,正常處理。

簡單路由功能

  正如上文中的路徑 /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是何方神聖呢?讓我們進入下一節吧。

路由排程器(ILayIMDispatcher)

  這個排程器翻譯的不是很準確,不過大家理解就好。如果不理解的話,看下面的圖就知道了。

  

  介面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 圍觀 提意見。

相關文章