微信小程序給我們提供了(le/liǎo)一個(gè)很好的(de)開發平台,可以(yǐ)用于(yú)展現各種數據和(hé / huò)實現豐富的(de)功能,通過小程序的(de)請求Web API 平台獲取JSON數據後,可以(yǐ)在(zài)小程序界面上(shàng)進行數據的(de)動态展示。在(zài)數據的(de)關鍵 一環中,我們設計和(hé / huò)編寫Web API平台是(shì)非常重要(yào / yāo)的(de),通過這(zhè)個(gè)我們可以(yǐ)實現數據的(de)集中控制和(hé / huò)管理,本篇随筆介紹基于(yú)Asp.NET MVC的(de)Web API接口層的(de)設計和(hé / huò)常見接口代碼的(de)展示,以(yǐ)便展示我們常規Web API接口層的(de)接口代碼設計、參數的(de)處理等内容。
1、Web API整體性的(de)架構設計
我們整體性的(de)架構設計,包含一個(gè)Web管理後台、一個(gè)Web API統一接口層、當然還有數據庫什麽,另外還有一個(gè)小程序客戶端。
整個(gè)體系以(yǐ)Web API爲(wéi / wèi)主提供服務,同時(shí)後台管理系統通過各種界面維護着數據的(de)增删改等基礎管理工作。
Web API的(de)分層,我們可以(yǐ)通過下圖來(lái)了(le/liǎo)解具體的(de)分層結構。
随着基于(yú)JSON格式的(de)Web API的(de)廣泛應用,越來(lái)越多的(de)企業采用Web API接口服務層,作爲(wéi / wèi)統一接口的(de)核心所在(zài),也(yě)成爲(wéi / wèi)Web API核心層。基于(yú)JSON格式的(de)接口,可以(yǐ)廣泛地(dì / de)、跨平台的(de)應用于(yú)IOS、安卓等移動端,也(yě)可以(yǐ)應用在(zài)常規的(de)Web業務系統,Winform業務系統、微信應用、微信小程序等方方面面,因此企業内部形成自己是(shì)的(de)一套Web API标準和(hé / huò)詳細的(de)文檔非常重要(yào / yāo)。
我們可以(yǐ)細化爲(wéi / wèi)下面的(de)架構設計圖,所有模塊均圍繞着Web API 接口層進行擴展,底層的(de)數據存儲對上(shàng)層的(de)應用是(shì)完全透明,我們可以(yǐ)根據需要(yào / yāo)拆分各種業務數據庫,以(yǐ)及使用我們認爲(wéi / wèi)合适的(de)數據庫。
其中我們在(zài)Web API接口層上(shàng)還看到(dào)一個(gè)微信消息交互的(de)模塊,這(zhè)個(gè)模塊我們爲(wéi / wèi)了(le/liǎo)方便域名端口的(de)處理,和(hé / huò)Web API 是(shì)統一放在(zài)一起的(de),它負責和(hé / huò)騰訊微信服務器進行消息的(de)交互處理,從而(ér)實現各種消息推送處理。
2、基于(yú)Asp.NET MVC的(de)Web API接口的(de)實現
1)GET方式
GET方式,接口參數包括有零或一個(gè)參數,以(yǐ)及多個(gè)參數的(de)方式,返回的(de)值可以(yǐ)是(shì)簡單的(de)字符串等基礎類型,也(yě)可以(yǐ)是(shì)複雜的(de)自定義對象類型等,如下面幾種接口代碼所示。
/// <summary> /// 簡單的(de)GET方式獲取數據 /// </summary> /// <param name="id">字符串ID</param> /// <param name="token">接口訪問令牌</param> /// <returns>返回字符串值</returns> [HttpGet] public string Test(string id, string token) { return string.Format("返回結果, id:{0}", id); } /// <summary> /// 多個(gè)參數的(de)GET方式獲取數據 /// </summary> /// <param name="id">字符串ID</param> /// <param name="name">名稱</param> /// <param name="token">接口訪問令牌</param> /// <returns>返回字符串值</returns> [HttpGet] public string TestMulti(string id, string name, string token) { return string.Format("返回結果, id:{0} name:{1}", id, name); } /// <summary> /// 參數測試GET返回自定義實體類對象 /// </summary> /// <param name="id">字符串ID</param> /// <param name="token">接口訪問令牌</param> /// <returns>返回自定義實體類對象</returns> [HttpGet] public virtual CommonResult TestObject(string id, string token) { return new CommonResult() { Data1 = id, Success = true }; } /// <summary> /// 測試GET返回列表對象 /// </summary> /// <param name="token">接口訪問令牌</param> /// <returns>返回列表對象</returns> [HttpGet] public List<string> TestAction(string token) { List<string> list = new List<string>() { "123", "234", "345" }; return list; }
2)POST方式
POST方式,同樣也(yě)和(hé / huò)GET方式的(de)一樣,接口參數包括有零或一個(gè)參數,以(yǐ)及多個(gè)參數的(de)方式,返回的(de)值可以(yǐ)是(shì)簡單的(de)字符串等基礎類型,也(yě)可以(yǐ)是(shì)複雜的(de)自定義對象類型等,這(zhè)就(jiù)是(shì)幾種常規的(de)接口處理。但是(shì),對于(yú)多個(gè)參數的(de)接口定義,我們需要(yào / yāo)對它們進行轉換處理,需要(yào / yāo)使用JObject param的(de)方式進行定義,這(zhè)樣可以(yǐ)很好對多個(gè)參數或者自定義的(de)實體類參數進行解析。
下面是(shì)幾種常規的(de)POST接口定義方式。
/// <summary> /// 測試使用POST方式提交數據,參數輸入爲(wéi / wèi)多個(gè),使用JObject處理 /// </summary> /// <returns>返回字符串</returns> [HttpPost] public string TestPost(JObject param, string token) { dynamic obj = param; string id = obj.id; if (obj != null) { return string.Format("返回結果, id:{0}", id); } else { throw new MyApiException("傳遞參數出(chū)現錯誤"); } } /// <summary> /// 測試使用POST方式提交數據,參數輸入爲(wéi / wèi)多個(gè),使用JObject處理 /// </summary> /// <returns>返回參數計算數值</returns> [HttpPost] public int TestPostSimple(JObject param) { dynamic obj = param; if (obj != null) { return obj.x * obj.y * 10; } else { throw new MyApiException("傳遞參數出(chū)現錯誤"); } } /// <summary> /// 測試POST的(de)方法,方法統一采用JObject param 方式定義,包含一個(gè)msg字符串對象,以(yǐ)及一個(gè)CListItem對象 /// </summary> /// <returns>返回一個(gè)通用的(de)CommonResult對象,包括Data1,Data2,Data3的(de)信息</returns> [HttpPost] public CommonResult TestPostObject(JObject param) { dynamic obj = param; if (obj != null) { string msg = obj.msg; //消息對象 //如果obj.item爲(wéi / wèi)類對象,那麽需要(yào / yāo)轉換爲(wéi / wèi)JObject然後使用ToObject轉換爲(wéi / wèi)對應類型 CListItem item = ((JObject)obj.item).ToObject<CListItem>(); var result = new CommonResult(true, msg); result.Data1 = msg; result.Data2 = item.Text; result.Data3 = item.Value; return result; } else { throw new MyApiException("傳遞參數出(chū)現錯誤"); } } /// <summary> /// 修改分組,方法統一采用JObject param 方式定義,包括一個(gè)字符串對象contactId,一個(gè)字符串列表對象groupIdList /// </summary> /// <returns>返回一個(gè)通用的(de)對象</returns> [HttpPost] public CommonResult TestPostList(JObject param) { dynamic obj = param; if (obj != null) { string contactId = obj.contactId; //聯系人(rén)ID //如果是(shì)List<string>的(de)類似列表,不(bù)能直接轉換,先轉換爲(wéi / wèi)JArray後使用ToObject轉換爲(wéi / wèi)對應列表 List<string> groupIdList = ((JArray)obj.groupIdList).ToObject<List<string>>(); var result = true; //BLLFactory<Address>.Instance.ModifyAddressGroup(contactId, groupIdList); return new CommonResult(result); } else { throw new MyApiException("傳遞參數出(chū)現錯誤,請檢查是(shì)否包含了(le/liǎo)contactId和(hé / huò)groupIdList"); } }
接口類,我們一般把類繼承自自己的(de)API接口基類,并對它的(de)異常處理進行處理,以(yǐ)便對錯誤統一格式回應,如下接口類的(de)代碼定義所示。
/// <summary> /// 此控制器用來(lái)詳細介紹各種GET/POST的(de)接口設計 /// 對于(yú)GET方式,方法可以(yǐ)接受多個(gè)參數 /// 對于(yú)POST方式,方法如果有參數使用POST方式,統一采用JObject param對象參數。 /// 如果POST方式有多個(gè)參數,如Web API接口加token,則需要(yào / yāo)客戶端把該參數追加在(zài)URL上(shàng),如url?token=123,然後在(zài)使用POST操作 /// </summary> [ExceptionHandling] public class TestController : BaseApiController
其中ExceptionHandling是(shì)我們的(de)統一異常過濾處理定義,代碼如下所示。
/// <summary> /// API自定義錯誤過濾器屬性 /// </summary> public class ExceptionHandlingAttribute : ExceptionFilterAttribute { /// <summary> /// 統一對調用異常信息進行處理,返回自定義的(de)異常信息 /// </summary> /// <param name="context">HTTP上(shàng)下文對象</param> public override void OnException(HttpActionExecutedContext context) { //自定義異常的(de)處理 MyApiException ex = context.Exception as MyApiException; if (ex != null) { //記錄關鍵的(de)異常信息 LogHelper.Error(context.Exception); throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { //封裝處理異常信息,返回指定JSON對象 Content = new StringContent(new BaseResultJson(ex.Message, false, ex.errcode).ToJson()), ReasonPhrase = "Exception" }); } //常規異常的(de)處理 string msg = string.IsNullOrEmpty(context.Exception.Message) ? "接口出(chū)現了(le/liǎo)錯誤,請重試或者聯系管理員" : context.Exception.Message; throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(msg), ReasonPhrase = "Critical Exception" }); } }
3)小程序端代碼處理
小程序端主要(yào / yāo)是(shì)通過JS代碼進行處理,實現數據的(de)獲取及提交處理等。
如我們列舉一個(gè)代表性的(de)POST處理代碼,如下所示。
//測試POst方法 wx.request({ url: 'http://localhost:27206/api/SmallApp/Test/TestPostObject', data: { msg : '測試内容', item: {Text :'Text', Value:'testValue'} }, header: {'Content-Type': 'application/json' }, method: 'POST', success: function (res) { console.log(res.data); } });
而(ér)對于(yú)GET方式,我們的(de)小程序調用方式如下所示。
getFilms: function(start) { console.log('start:' + start); var that = this wx.request({ url: 'http://www.iqidi.com/api/h5/test/movies', data: { offset: start, type: 'hot', limit: that.data.limit }, header: { 'Content-Type': 'application/json' }, success: function (res) { console.log(res.data) var data = http://www.wxapp-union.com/res.data.data; console.log(data); if (data.movies.length === 0) { that.setData({ hasMore: false, hideLoading :true, }) } else { that.setData({ films: that.data.films.concat(data.movies), start: that.data.start + data.movies.length, hasMore: true, hideLoading :true, }); } } })
以(yǐ)上(shàng)就(jiù)是(shì)我們常規接口(單個(gè)參數或者多個(gè)參數,簡單對象和(hé / huò)複雜對象的(de)處理)的(de)定義代碼,希望讀者在(zài)開發Web API接口的(de)時(shí)候,可以(yǐ)有所幫助。