前言
好久沒寫部落格了。前階段看了下Hangfire元件,後來對其程式碼比較感興趣,當時不太瞭解他如何生成的頁面和一些訪問請求等。後來看了下原始碼,發現原來是 OWIN 在搞怪。於是乎開始深入研究Hangfire原始碼,當然只是Route和Razor部分,具體他的核心業務後臺執行任務我沒研究。因為,我想到了之前做的LayIM的一個對接。那時候寫的程式碼和專案耦合度太高,於是冒出一個寫LayIM Owin外掛的想法。直接開工吧。
開工
主要參考了Hangfire的Dashboard部分。 https://github.com/HangfireIO/Hangfire/tree/master/src/Hangfire.Core/Dashboard
與之前的程式碼相比,這些程式碼都沒有放入WebUI 端中,而且,分層也比較少。就一個 Owin中介軟體和SqlServer的實現。程式碼結構如下:(我自己當然寫不出啦,參考上文連結中的程式碼)
用過OWIN的都知道,就是通過IAppBuilder介面註冊中介軟體。其實還是挺複雜的,雖然照著寫了出來。為了避免誤導觀眾,我就不多解釋了。不過路由那一塊我我可以說一下我的理解。
首先定義路由集合,集合中包含所有註冊的路徑和實現 ILayIMDispatcher 介面的 Dispatcher。
public class RouteCollection { private readonly List<Tuple<string, ILayimDispatcher>> _dispatchers = new List<Tuple<string, ILayimDispatcher>>(); /// <summary> /// 註冊路由 /// </summary> /// <param name="pathTemplate">路由地址</param> /// <param name="dispatcher">處理方法</param> public void Add(string pathTemplate, ILayimDispatcher dispatcher) { Error.ThrowIfNull(pathTemplate, nameof(pathTemplate)); Error.ThrowIfNull(dispatcher, nameof(dispatcher)); _dispatchers.Add(new Tuple<string, ILayimDispatcher>(pathTemplate, dispatcher)); } /// <summary> /// 根據Path尋找對應的Dispatcher /// 通過正規表示式來找到匹配的結果 /// </summary> /// <param name="path">路徑</param> /// <returns></returns> public Tuple<ILayimDispatcher, Match> FindDispatcher(string path) { if (path.Length == 0) path = "/"; foreach (var dispatcher in _dispatchers) { var pattern = dispatcher.Item1; if (!pattern.StartsWith("^", StringComparison.OrdinalIgnoreCase)) { pattern = $"^{pattern}"; } if (!pattern.EndsWith("$", StringComparison.OrdinalIgnoreCase)) { pattern += "$"; } 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; } }
請求,進入中介軟體的處理程式碼:
var owinContext = new OwinContext(env); var context = new OwinLayimContext(storage,options, env); var path = owinContext.Request.Path.Value; //匹配路由 var findResult = routes.FindDispatcher(path); //如果沒有匹配到,執行下一個 if (findResult == null) { return next(env); } //匹配成功之後執行 Dispatch context.UriMatch = findResult.Item2; //執行具體disptach方法,返回相應結果 return findResult.Item1.Dispatch(context);
舉例來說:Layim中的初始化介面。定義為 /init 需要引數為 id. 那麼在路由中,註冊如下:
//AddQuery只是又封裝了一層,內部還是呼叫了Add方法。 Routes.AddQuery<long>("/init", "id", (context, uid) => { //返回處理結果 });
在上邊的例子中,AddQuery方法,註冊了一個 SingleParameterQueryDispatcher<T> 的處理類。 他的作用就是處理擁有單個引數的型別為T的請求。具體程式碼如下:
public async Task Dispatch(LayimContext context) { var request = context.Request; var response = context.Response; var parameterValue = request.GetQuery(_parameterName); //如果不是Get請求,返回方法不允許 if (!CommandMethod.Equals(request.Method, StringComparison.OrdinalIgnoreCase)) { response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; await Task.FromResult(false); } //返回結果為"application/json"; context.Response.ContentType = "application/json"; //將引數轉化為相應的型別,有 null 異常 T value = (T)Convert.ChangeType(parameterValue, typeof(T)); //執行具體處理函式 var result = _command(context, value); //序列化結果 var json = context.Options.Serializer.SerializeObject(result); //返回 await context.Response.WriteAsync(json); }
總結
簡單了介紹一下路由部分的內容,相信很多小夥伴也是看的雲裡霧裡的。看懂OWIN機制,就能夠懂了。我也是大部分借鑑(抄襲)了Hangfire中的程式碼。
基本思路:
- 註冊路由
- 實現路由匹配方法
- 找到對應的處理器
- 處理方法,返回結果
在不懂的話,建議直接閱讀原始碼,閱讀原始碼能有意想不到的收穫。
附:LayIM.NetClient中介軟體的使用方法。其中IM通訊我使用了融雲,所以在其中也做了一些封裝。
public class Startup { public void Configuration(IAppBuilder app) { //使用SQL Server GlobalConfiguration.Configuration.UseSqlServer("LayIM_Connection"); //使用layim api 6tnym1brnmpt7 app.UseLayimApi("/layim", new LayimOptions { RongCloudSetting = new RongCloudSetting() }); } }
LayIM中註冊的路由如下:
執行結果:
獲取融雲token的方法:
程式碼執行正常
最後:我覺得Owin中介軟體真的很方便。首先能夠避免web專案中寫太多的重複的業務邏輯。(當然,layim部分抽出成api也可以) 通過封裝成 LayIM.NetClient中介軟體以後,不管是webform還是mvc都可以通過Owin來適配。後邊還會繼續完善。今天就到此為止吧~~