標題的名稱定義不知道是否準確,不過我想表達的意思就是使用Task特性來同時請求多個不一樣的接口,而後合併數據;我想這種場景的開發對於對接過其餘公司接口的人不會陌生,本人也是列屬於以內,更多的是使用最原始的異步委託的方法去處理,今天抽空寫了一個使用4.5新特性Task來處理這種場景;各位看客有什麼疑問或者好的建議及分享請博客通知,謝謝。html
A.項目結構圖git
B.namespace Pm.V.PM_BLL下面的BaseClass定義以下:github
1 public abstract class BaseClass 2 { 3 4 #region 初始化xml配置信息 BaseClass 5 6 /// <summary> 7 /// 初始化xml配置信息 8 /// </summary> 9 /// <param name="xmlConf"></param> 10 public BaseClass(string xmlConfigPath) 11 { 12 13 try 14 { 15 16 if (string.IsNullOrEmpty(xmlConfigPath)) 17 { 18 19 //默認各個Xml配置 20 var defaultConfigFolder = "PluginXml"; 21 var baseAddr = AppDomain.CurrentDomain.BaseDirectory; 22 xmlConfigPath = Path.Combine(baseAddr, defaultConfigFolder, this.GetType().Name + ".xml"); 23 } 24 25 XmlDocument doc = new XmlDocument(); 26 doc.Load(xmlConfigPath); 27 28 Config = new BaseConfig(); 29 Config.Url = doc.SelectSingleNode("//Pm/Url") == null ? "" : doc.SelectSingleNode("//Pm/Url").InnerXml; 30 Config.UserName = doc.SelectSingleNode("//Pm/UserName") == null ? "" : doc.SelectSingleNode("//Pm/UserName").InnerXml; 31 Config.UserKey = doc.SelectSingleNode("//Pm/UserKey") == null ? "" : doc.SelectSingleNode("//Pm/UserKey").InnerXml; 32 33 Config.Doc = doc; 34 } 35 catch (Exception ex) 36 { 37 38 throw new Exception(ex.Message); 39 } 40 41 } 42 43 /// <summary> 44 /// xml配置信息 45 /// </summary> 46 public BaseConfig Config; 47 #endregion 48 49 #region 獲取文章信息 _GetArticles +BaseResponse 50 51 /// <summary> 52 /// 獲取文章信息 53 /// </summary> 54 /// <param name="request"></param> 55 /// <returns></returns> 56 public virtual MoArticlesResponse _GetArticles(object request) 57 { 58 return null; 59 } 60 #endregion 61 62 } 63 64 /// <summary> 65 /// xml配置文件信息 66 /// </summary> 67 public class BaseConfig 68 { 69 70 /// <summary> 71 /// 接口地址 72 /// </summary> 73 public string Url { get; set; } 74 75 /// <summary> 76 /// 帳號 77 /// </summary> 78 public string UserName { get; set; } 79 80 /// <summary> 81 /// 密碼|祕鑰 82 /// </summary> 83 public string UserKey { get; set; } 84 85 /// <summary> 86 /// xml文件所有信息 87 /// </summary> 88 public XmlDocument Doc { get; set; } 89 90 }
主要是在實例的時候讀取各個業務模塊的配置文件,初始化一些經常使用而且是公共的屬性信息;其次建立了一個虛方法_GetArticles,注意裏面的參數是object這個將再後面的時候重提;json
C.下面就是請求第三方業務實現類的代碼,這裏測試的時候分別使用api
1.CnblogsClass類抓取博客園首頁的博客列表信息數組
1 /// <summary> 2 /// 博客園信息(若是涉及到信息來源問題,請及時聯繫開源做者,謝謝) 3 /// </summary> 4 public class CnblogsClass : BaseClass 5 { 6 /// <summary> 7 /// 初始化xml配置 8 /// </summary> 9 public CnblogsClass() : base("") { } 10 11 #region 獲取文章信息 _GetArticles +BaseResponse 12 13 /// <summary> 14 /// 獲取文章信息 15 /// </summary> 16 /// <param name="request"></param> 17 /// <returns></returns> 18 public override MoArticlesResponse _GetArticles(object obj) 19 { 20 #region 初始化信息 21 22 var request = new MoArticlesRequest(); 23 var response = new MoArticlesResponse(); 24 var sbLog = new StringBuilder(string.Empty); 25 var url = this.Config.Url; //這裏獲取配置文件的url 26 #endregion 27 28 try 29 { 30 31 #region 接口驗證數據 32 33 request = obj as MoArticlesRequest; 34 if (request == null) 35 { 36 37 response.ErrorCode = (int)EHelper.PmException.獲取數據爲空; 38 response.ErrorMsg = EHelper.PmException.獲取數據爲空.ToString(); 39 return response; 40 } 41 42 #endregion 43 44 #region 請求數據 45 46 47 sbLog.AppendFormat("請求地址:{0}\n", url); 48 var result = PublicClass._HttpGet(url); //這裏通常都是post數據到第三方接口,測試沒有第三方能夠使用,因此使用抓取博客園首頁數據 49 sbLog.AppendFormat("返回信息:{0}\n", result); 50 #endregion 51 52 #region 解析 53 54 //使用正則解析數據 55 var rgs = Regex.Matches(result, "class=\"titlelnk\"\\s+href=\"(?<link>http://www(\\w|\\.|\\/)+\\.html)\"[^>]+>(?<title>[^<]+)<\\/a>[^D]+a>(?<des>[^<]+)[^D]+lightblue\">(?<author>\\w+)<\\/a>[^D]+發佈於(?<publishtime>\\s+(\\d|-|\\s|:)+)[^<]+"); 56 57 if (rgs.Count <= 0) 58 { 59 60 response.ErrorCode = (int)EHelper.PmException.獲取數據爲空; 61 response.ErrorMsg = EHelper.PmException.獲取數據爲空.ToString(); 62 return response; 63 } 64 65 foreach (Match item in rgs) 66 { 67 68 var article = new MoArticle(); 69 70 article.Author = item.Groups["author"].Value; 71 article.LinkUrl = item.Groups["link"].Value; 72 article.Title = item.Groups["title"].Value; 73 article.PublishTime = item.Groups["publishtime"].Value; 74 // article.Des = item.Groups["des"].Value; 75 article.DataType = (int)EHelper.DataType.博客園; 76 77 if (response.MoArticles.Count > 5) { continue; } 78 response.MoArticles.Add(article); 79 } 80 81 response.IsSuccess = true; 82 #endregion 83 } 84 catch (Exception ex) 85 { 86 87 sbLog.AppendFormat("異常信息:{0}\n", ex.Message); 88 response.ErrorCode = (int)EHelper.PmException.獲取數據異常; 89 response.ErrorMsg = EHelper.PmException.獲取數據異常.ToString(); 90 } 91 finally 92 { 93 94 #region 第三方原始信息-日誌 95 96 //PublicClass._WriteLog(sbLog.ToString(), "Cnblogs"); 97 98 #endregion 99 } 100 return response; 101 } 102 #endregion 103 }
.注意這裏使用 : base("")直接繼承了上面說的父類的方法,來初始化配置信息(固然這個可能不算知識點)框架
.接下來就是實現的_GetArticles方法裏面PublicClass._HttpGet方法封裝的HttpClient獲取博客園的數據異步
.解析了返回的數據信息(這裏使用的正則,可能有些同窗以爲正則可能還不太熟悉,能夠自行百度參考分析下)async
.記錄日誌,我這裏是記錄的文本日誌暫時註釋了,由於怕抓取的信息多,忘記刪除佔用空間ide
以上幾點就是實際狀況中常常遇到的步奏,這個處理步奏在歷來沒有對接過第三方接口的人仍是值得學習的
2.HuJiangClass類是抓取了博客園中.Net第一頁的數據,步奏和方法和上面相同,請關注代碼部分
1 public class HuJiangClass : BaseClass 2 { 3 /// <summary> 4 /// 初始化xml配置 5 /// </summary> 6 public HuJiangClass() : base("") { } 7 8 9 #region 獲取文章信息 _GetArticles +BaseResponse 10 11 /// <summary> 12 /// 獲取文章信息 13 /// </summary> 14 /// <param name="request"></param> 15 /// <returns></returns> 16 public override MoArticlesResponse _GetArticles(object obj) 17 { 18 #region 初始化信息 19 20 var request = new MoArticlesRequest(); 21 var response = new MoArticlesResponse(); 22 var sbLog = new StringBuilder(string.Empty); 23 var url = this.Config.Url; //這裏獲取配置文件的url 24 #endregion 25 26 try 27 { 28 29 #region 接口驗證數據 30 31 request = obj as MoArticlesRequest; 32 if (request == null) 33 { 34 35 response.ErrorCode = (int)EHelper.PmException.獲取數據爲空; 36 response.ErrorMsg = EHelper.PmException.獲取數據爲空.ToString(); 37 return response; 38 } 39 40 #endregion 41 42 #region 請求數據 43 44 sbLog.AppendFormat("請求地址:{0}\n", url); 45 var result = PublicClass._HttpGet(url); //這裏通常都是post數據到第三方接口,測試沒有第三方能夠使用,因此使用抓取博客園首頁數據 46 sbLog.AppendFormat("返回信息:{0}\n", result); 47 #endregion 48 49 #region 解析 50 51 //使用正則解析數據 52 var rgs = Regex.Matches(result, "class=\"titlelnk\"\\s+href=\"(?<link>http://www(\\w|\\.|\\/)+\\.html)\"[^>]+>(?<title>[^<]+)<\\/a>[^D]+post_item_summary\">(?<des>[^<]+)[^D]+lightblue\">(?<author>\\w+)<\\/a>[^D]+發佈於(?<publishtime>\\s+(\\d|-|\\s|:)+)[^<]+"); 53 54 if (rgs.Count <= 0) 55 { 56 57 response.ErrorCode = (int)EHelper.PmException.獲取數據爲空; 58 response.ErrorMsg = EHelper.PmException.獲取數據爲空.ToString(); 59 return response; 60 } 61 62 foreach (Match item in rgs) 63 { 64 65 var article = new MoArticle(); 66 67 article.Author = item.Groups["author"].Value; 68 article.LinkUrl = item.Groups["link"].Value; 69 article.Title = item.Groups["title"].Value; 70 article.PublishTime = item.Groups["publishtime"].Value; 71 // article.Des = item.Groups["des"].Value; 72 article.DataType = (int)EHelper.DataType.博客園NET技術; 73 if (response.MoArticles.Count > 5) { continue; } 74 response.MoArticles.Add(article); 75 } 76 77 response.IsSuccess = true; 78 #endregion 79 } 80 catch (Exception ex) 81 { 82 83 sbLog.AppendFormat("異常信息:{0}\n", ex.Message); 84 response.ErrorCode = (int)EHelper.PmException.獲取數據異常; 85 response.ErrorMsg = EHelper.PmException.獲取數據異常.ToString(); 86 } 87 finally 88 { 89 90 #region 第三方原始信息-日誌 91 92 //PublicClass._WriteLog(sbLog.ToString(), "Cnblogs"); 93 94 #endregion 95 } 96 return response; 97 } 98 #endregion 99 100 }
D.今天要講的主要內容來了,以上算是過分,讓人瞭解對接第三方接口的一些處理步奏和簡單的封裝吧;這裏將看到的是Pm.Api.Controllers空間下BlogsController裏面的Post方法,代碼如:
1 // GET api/<controller> 2 public IEnumerable<string> Get() 3 { 4 return new string[] { "歡迎使用-神牛步行3開源框架" }; 5 } 6 7 // POST api/<controller> 8 public async Task<HttpResponseMessage> Post() 9 { 10 HttpResponseMessage response = new HttpResponseMessage(); 11 var baseResponse = new BaseResponse(); 12 try 13 { 14 #region 驗證 15 16 //獲取post數據 17 HttpContent content = Request.Content; 18 var param = await content.ReadAsStringAsync(); 19 var baseRequest = Newtonsoft.Json.JsonConvert.DeserializeObject<BaseRequest>(param); 20 if (string.IsNullOrEmpty(baseRequest.FunName)) 21 { 22 baseResponse.ErrorMsg = EHelper.PmException.請求參數爲空.ToString(); 23 baseResponse.ErrorCode = (int)EHelper.PmException.請求參數爲空; 24 response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse)); 25 return response; 26 } 27 else if (string.IsNullOrEmpty(baseRequest.UserName)) 28 { 29 30 baseResponse.ErrorMsg = EHelper.PmException.帳號不正確.ToString(); 31 baseResponse.ErrorCode = (int)EHelper.PmException.帳號不正確; 32 response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse)); 33 return response; 34 } 35 else if (!Enum.IsDefined(typeof(EHelper.DataType), baseRequest.DataType)) 36 { 37 baseResponse.ErrorMsg = EHelper.PmException.參數不合法.ToString(); 38 baseResponse.ErrorCode = (int)EHelper.PmException.參數不合法; 39 response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse)); 40 return response; 41 } 42 //驗證帳號及token 43 44 #endregion 45 46 #region 業務 47 48 var dataTypes = Enum.GetValues(typeof(EHelper.DataType)); 49 switch (baseRequest.FunName) 50 { 51 //獲取文章集合信息 52 case "_GetArticles": 53 54 //json反序列獲取數據 55 var r_GetArticles = Newtonsoft.Json.JsonConvert.DeserializeObject<MoArticlesRequest>(param); 56 57 //初始化任務量 58 var tasks = new Task<MoArticlesResponse>[baseRequest.DataType == 0 ? dataTypes.Length : 1]; 59 var proxy = new Pm_Proxy(); 60 var j = 0; //真實任務座標 61 for (int i = 0; i < dataTypes.Length; i++) 62 { 63 var item = dataTypes.GetValue(i); 64 var nType = Convert.ToInt32(item); 65 if (nType != baseRequest.DataType && 0 != baseRequest.DataType) { continue; } 66 67 //使用任務作並行 68 var dataType = proxy._DataType(nType); 69 var task = Task.Factory.StartNew<MoArticlesResponse>(dataType._GetArticles, r_GetArticles); 70 tasks[j] = task; 71 j++; 72 } 73 //30s等待 74 Task.WaitAll(tasks, 1000 * 1 * 30); 75 76 //獲取任務執行的結果(整合數據) 77 var articles = new MoArticlesResponse(); 78 foreach (var task in tasks) 79 { 80 if (!task.IsCompleted) { continue; } 81 articles.MoArticles.AddRange(task.Result.MoArticles); 82 } 83 84 articles.IsSuccess = articles.MoArticles.Count > 0; 85 baseResponse = articles; 86 break; 87 88 default: 89 break; 90 } 91 response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse)); 92 #endregion 93 } 94 catch (Exception ex) 95 { 96 baseResponse.ErrorMsg = EHelper.PmException.獲取數據異常.ToString(); 97 baseResponse.ErrorCode = (int)EHelper.PmException.獲取數據異常; 98 } 99 return response; 100 }
1.首先使用了var baseRequest = Newtonsoft.Json.JsonConvert.DeserializeObject<BaseRequest>(param); 來第一次返序列化,獲得驗證的基本信息如帳號,token等
2.經常使用的接口形式使用參數節點名稱來肯定將要執行的方法,或者直接在節點值中標註方法的名稱,所以有了這麼一句switch (baseRequest.FunName)來判斷程序的走向
3.在此反序列獲得真實調用者post給接口的數據(上面第一次反序列使用的是基類,基類裏面就包含了驗證須要的屬性,爲何這裏不直接使用第一次反序列的對象呢,由於這裏將要傳遞給後面方法參數的值是子類裏面封裝的)
4.開始定義Task任務的數量,通常根據有幾個第三方接口第一幾個吧,Task<MoArticlesResponse>[]保證後面產生的任務量
5.Task.Factory.StartNew<MoArticlesResponse>(dataType._GetArticles, r_GetArticles) 方法來建立任務,這裏要說的是dataType._GetArticles是以前上面說的請求第三方接口定義的方法,r_GetArticles這個是須要傳遞的值是object的,這也是StartNew固定的參數類型;再經過Task<MoArticlesResponse>[]存儲建立的任務;
6.Task.WaitAll(tasks, 1000 * 1 * 30);這個WaitAll是自帶的,意思是等待任務執行多少毫秒,也算是知識點吧,第一個參數是任務數組,是數組的形式,第二個是毫秒單位的等待時間
7.最後經過foreach (var task in tasks) 來循環整合task.Result返回的結果
以後是效果截圖DataType表示不一樣的數據來源:
大體就是這些吧,不知道說的東西是否說明白了,這就是處理對接第三方不一樣接口的業務邏輯,也是使用task來並行處理的方法;若是有朋友以爲可能說的不太好或者有什麼錯誤地方,還請直接發博客信息,謝謝;
源碼地址:https://github.com/shenniubuxing3/Niu.Pm