微(wēi)信小(xiǎo)程序給我們提供了(le)一(yī)個(gè)很(hěn)好©≠(hǎo)的(de)開(kāi)發平台,可(kě)以用(yòng)于展現∏δ↕(xiàn)各種數(shù)據和(hé)實現(xiàn)豐富的(de)≤∞功能(néng),通(tōng)過小(xiǎo)程序的(de)請(qǐng±§×)求Web API 平台獲取JSON數(shù)據≥α¶∞後,可(kě)以在小(xiǎo)程序界面上(shàng)↕ε進行(xíng)數(shù)據的(de)動态展示。在數(shùε™π)據的(de)關鍵 一(yī)環中,我們設計(jì)和(hé)編寫Web >Ωσ;API平台是(shì)非常重要(yào)的(de),通(tōng)過這(z™φ•∞hè)個(gè)我們可(kě)以實現(xiàn)數(shù)據的(de£±≠)集中控制(zhì)和(hé)管理(lǐ),本篇随筆(bǐ)介紹基于Asp.NET&nbα↑sp;MVC的(de)Web API接口層的(de♠☆€)設計(jì)和(hé)常見(jiàn)接口代碼的(de)展示,以便展示我們常規Web&π≥nbsp;API接口層的(de)接口代碼設計(jì)、參數(shù)的(de)處αα理(lǐ)等內(nèi)容。
1、Web API整體(tǐ)性的(de)架構設計(jì)
我們整體(tǐ)性的(de)架構設計(jì),包含一(yī↑φ)個(gè)Web管理(lǐ)後台、一(yī)個(g✔♦♦è)Web API統一(yī)接口層、當然還(hái)有(yǒu)數(shù€¥γ)據庫什(shén)麽,另外(wài)還(hái)有(yǒu)一(yī)個(gè)±•↔₹小(xiǎo)程序客戶端。
整個(gè)體(tǐ)系以Web API為(wèi)主提供<♠§服務,同時(shí)後台管理(lǐ)系統通(tōng)過各種界面維護著(zhe)∞₩↕數(shù)據的(de)增删改等基礎管理(lǐ)工(gōng)作(zuò)。
Web API的(de)分(fēn)層,我們可(kě)以通(tōng)過¥≠₹✔下(xià)圖來(lái)了(le)解具體(tǐ)的(de)分(fēn)層結構。
随著(zhe)基于JSON格式的(de)Web API的(de)廣♠♠←泛應用(yòng),越來(lái)越多(duō)的(de)企業(yè÷λ)采用(yòng)Web API接口服務層,作(zuò)為(wèi)統一β≥<$(yī)接口的(de)核心所在,也(yě)成為(wèi)Web γ→✘✔;API核心層。基于JSON格式的(de)接口,可(kě)以廣泛地(dì)、跨平台的(de×∏)應用(yòng)于IOS、安卓等移動端,也(yě)↔÷₩可(kě)以應用(yòng)在常規的(de)Web業(yè)∑∞✘務系統,Winform業(yè)務系統、微(wēi)信應用(yòng)、微(wēi)信小(x±♠iǎo)程序等方方面面,因此企業(yè)內(nèi)部形成自(zì)己是(shì)↔π☆↕的(de)一(yī)套Web API标準和(hé)詳細的(de)文(wén✘¥)檔非常重要(yào)。
我們可(kě)以細化(huà)為(wèi)下(xià)面的(de)架構設計(jε♥γ♥ì)圖,所有(yǒu)模塊均圍繞著(zhe)Web ¥∞✔API 接口層進行(xíng)擴展,底層的(de)數(shù)據存儲對₹×(duì)上(shàng)層的(de)應用(yòng)是(shì)完全透明♦™(míng),我們可(kě)以根據需要(yào)拆分(fēn)各種業(yè¥π)務數(shù)據庫,以及使用(yòng)我們認為(wèi)合适的(de)數(shù)據庫。
其中我們在Web API接口層上(shàng)還(h♦₩ái)看(kàn)到(dào)一(yī)個(gè)微(wēi)信消息交互的(de)模塊,✘→€&這(zhè)個(gè)模塊我們為(wèi)了(le)方便域名端口的(de)處理(lǐ),和→∏π(hé)Web API 是(shì)統一(yī)放(fàng)在一(yī)起↔©₹的(de),它負責和(hé)騰訊微(wēi)信服務器(qì)進行(xíng)消息的(de↔±)交互處理(lǐ),從(cóng)而實現(xiàn)各種消息推送處理(lǐ≤¶)。
2、基于Asp.NET MVC的(de)Web API接口的(de)實現( ☆xiàn)
1)GET方式
GET方式,接口參數(shù)包括有(yǒu)零或一(yī)個(gè)參數(σγ±↔shù),以及多(duō)個(gè)參數(shù)的(de)方式♦∞,返回的(de)值可(kě)以是(shì)簡單的(d∑ φe)字符串等基礎類型,也(yě)可(kě)以是(shì)複雜(zá)的(de)自±•(zì)定義對(duì)象類型等,如(rú)下(xià)面∑☆∑幾種接口代碼所示。
/// <summary> /// 簡單的(de)GET×σ←≈方式獲取數(shù)據 /// </summary> ♠π♠ /// <param name="id">字符串ID<≥σ↔✔;/param> /// <param n♠♣ame="token">接口訪問(wèn)令牌</param&g←♣t; /// <returns>返回字符串值→♦₽</returns> [HttpGet] pu>♠blic string Test(string id, string ¶♣≤'token) { return strin<↓→g.Format("返回結果, id:{0}", id); } $✔↕ /// <summary> ±ε /// 多(duō)個(gè)參數(shù)的(de)GET方式 ↑&獲取數(shù)據 /// </s≥ummary> /// <param name="id">←ε§&字符串ID</param> /// <♠ >param name="name">名稱</para♣¥m> /// <param name="token">®β×;接口訪問(wèn)令牌</param> /// <ret•>±urns>返回字符串值</returns> [HttpGet≈★] public string TestMulti(strγ™✘ing id, string name, strin≠$₹λg token) { retur βn string.Format("返回結果, id:{ 0} name:{1}", id, name); •π×Ω } /// <summary> //©∏∞/ 參數(shù)測試GET返回自(zì)定義實體(tǐ)類對(duì)象 €α₹ /// </summary> /"€σ≈// <param name="id">字符串ID</param> ♠☆✘₹ /// <param name="token">"↕<接口訪問(wèn)令牌</param> /// <÷♠↔returns>返回自(zì)定義實體(tǐ)類£₹對(duì)象</returns> [HttpGet] •'α≥ public virtual CommonResu÷ ♣lt TestObject(string id, string toke♠×✔n) { return new ←§™≤CommonResult() { Data1 = id, Success = true };•∑® } /// <summary> <$ /// 測試GET返回列表對(duì)象 // ✘/ </summary> /// <param na Ω®±me="token">接口訪問(wèn)令牌</param> 'Ω© /// <returns>返回列表對(duì)象&l'¶t;/returns> [Htt pGet] public List<string> TestActi★™±on(string token) { ↓♥ List<string> list = new Lis∑®↔δt<string>() { "123", "234", "345" }; ↕☆ return list; }
2)POST方式
POST方式,同樣也(yě)和(hé)GET方式的(§☆'₽de)一(yī)樣,接口參數(shù)包括有(yǒu)零或一(yī)個(gè)參數(£±shù),以及多(duō)個(gè)參數(shù)的(de)方式,返回的(de)值可(kě)♠×♥以是(shì)簡單的(de)字符串等基礎類型,也(yě)可(kě)以是(s↔ hì)複雜(zá)的(de)自(zì)定義對(duì)象類型等,這(zhè)就(∑®jiù)是(shì)幾種常規的(de)接口處理(lǐ)。但(dàn)是(shì)♣≤×,對(duì)于多(duō)個(gè)參數(shù)的(dδεe)接口定義,我們需要(yào)對(duì)它們進行(xíng)轉換處理(lǐ),需↑♣δ↕要(yào)使用(yòng)JObject param的(de ∑)方式進行(xíng)定義,這(zhè)樣可(kě)以很×♠(hěn)好(hǎo)對(duì)多(duō)個(gè)參數(shù)或者自≥←←(zì)定義的(de)實體(tǐ)類參數(shù)進行(xínγ¥<g)解析。
下(xià)面是(shì)幾種常規的(de)POST接口定義方式。
/// <summary> ↓€✘₹ /// 測試使用(yòng)POST方式提交數(shù)據,參數(shù)輸入為>§α(wèi)多(duō)個(gè),使用(yòng)JObject處理(l★∑ǐ) /// </summary> ε±♠ /// <returns>返回字符串</re₩ ✔§turns> [HttpPost] '₩¥ public string Test↑≥Post(JObject param, string token) { ✘®✔♦ dynamic obj = param; s γ<™tring id = obj.id; ₹™¥ if (obj != null) { ≤' return string.Fo÷γ rmat("返回結果, id:{0}", id); ↑π↑ } else ₹αα { throw new MyAp∞♠₩iException("傳遞參數(shù)出現(xiàn)錯(cuò)誤"); ≤Ω } } γα /// <summary> ↑☆®÷/// 測試使用(yòng)POST方式提交數(shù)據,參數(shù)輸入₽£↔為(wèi)多(duō)個(gè),使用(yòng)JObject處理(lǐ) ¥₩© /// </summary> /// <γ♠↕÷;returns>返回參數(shù)計(jì)算(suàn)數(shù)值&l₹®™™t;/returns> [HttpPost] pubβ™↑lic int TestPostSimple(JObject param) π♦ { dynamic obj =¶™ param; if (obj != null) ☆← { return obj.x * obj.y *<$€ 10; } else σ$ { throw new MyApiExc&€₽eption("傳遞參數(shù)出現(xiàn)錯(cuò)誤"); ↑≥♦λ } } /// &l©∑₽t;summary> /// 測試POST的(de)方法,方法統一∞ ₹&(yī)采用(yòng)JObject param 方≥$式定義,包含一(yī)個(gè)msg字符串對(duì)象,以及一(yī₽πφ)個(gè)CListItem對(duì)象 /// </s≠ βummary> /// <returγ×±ns>返回一(yī)個(gè)通(tōng)用(yòng)的(de)Comm$™←onResult對(duì)象,包括Data1,Data2,D÷★ata3的(de)信息</returns> [÷←HttpPost] public CommonResult TestPostO÷πλ®bject(JObject param) { γ dynamic obj = p÷®aram; if (obj != null) ® "© { string msg = obj.msg; ↔ε"☆//消息對(duì)象 ™≈ //如(rú)果obj.item為(wèi)類對(duì)象, ☆≥≈那(nà)麽需要(yào)轉換為(wèi)JObject然後使用λ→ (yòng)ToObject轉換為(wèi)對(duì)應類∏¶<型 CListItem ≠↑item = ((JObject)obj.item).ToObject<CListI♣₹tem>(); var result = ne"αw CommonResult(true, msg); <©®β result.Data1 = msg; ✘≥ result.Data2 = item.Text; ∏→± result.Data3 = item.Value; ∏'← return resul♦↓≠αt; } σ®$± else { ☆←™© throw new MyApiException("傳遞參數(shù)出現(xiφ™àn)錯(cuò)誤"); } } ™>• /// <summary> /// 修改分(≤ ♠fēn)組,方法統一(yī)采用(yòng)JObject param 方式定義,包括一(yī)個α'(gè)字符串對(duì)象contactId,一(→&×yī)個(gè)字符串列表對(duì)象groupIdList ± /// </summary> /// <retuΩ≤rns>返回一(yī)個(gè)通(tōng)用(yòng)的(de)對(duì)象&≈∑♣εlt;/returns> [HttpPost] pu÷≤blic CommonResult TestPostList(JObje•£$ct param) { dynamic obj =××" param; if (obj != null) ↕÷α { string contactId ="¶ obj.contactId; //聯系人(rén)ID €€ //如(rú)果是(shì)List<strinβ₹<$g>的(de)類似列表,不(bù)能(néng)直接轉換,先轉換為(wèi)JArr∑☆•≠ay後使用(yòng)ToObject轉換為(wèi)對(duìδ∞)應列表 List<string> groupI∏λ↔dList = ((JArray)obj.groupIdList>Ω).ToObject<List<string>φ&•>(); var re φαsult = true; //BLLFactory<Address&g♦₽t;.Instance.ModifyAddressGrou♠♦p(contactId, groupIdList); ₩return new CommonResult(result); ←₩ } else ≥↔₩ { throw new MyApiExceptio≥ε♣♠n("傳遞參數(shù)出現(xiàn)錯(cuò)誤,請(qǐng)檢查是(shì)否包含了→δ(le)contactId和(hé)groupIdList"); ₩✘ } }
接口類,我們一(yī)般把類繼承自(zì)自(zì)己的(d♥" ≈e)API接口基類,并對(duì)它的(de)異常處理(lǐ)進行(xíng)處理(l&★☆₩ǐ),以便對(duì)錯(cuò)誤統一(yī)格式回應,如(rú)下(x©ε↓ià)接口類的(de)代碼定義所示。
/// <summary> /// 此控制(zhì)器(qì α)用(yòng)來(lái)詳細介紹各種GET/POST的(de)接口設計(jì) ≠ /// 對(duì)于GET方式,方法可(kě)以接受多(duō♦↑ ¶)個(gè)參數(shù) /// 對(duì)于POS∑δT方式,方法如(rú)果有(yǒu)參數(shù)使用(yòng)POST方式,統一(yī)采用"ε(yòng)JObject param對(duì)象參數(shù★→')。 /// 如(rú)果POST方式有(yǒu)多(duō$€ε&)個(gè)參數(shù),如(rú)Web API¶₹✔←接口加token,則需要(yào)客戶端把該參數(s™↑hù)追加在URL上(shàng),如(rú)url¶§✔?token=123,然後在使用(yòng)POST操作(zuò) /// <₩/summary> [ExceptionHandling] γ♦β public class TestController : BaseApiController
其中ExceptionHandling是(shì)我們的(de)統一(yī)異常過濾處理β"₹©(lǐ)定義,代碼如(rú)下(xià)所示。
/// <summary> /// API自(zì)定義錯(cuò)誤過濾×¶器(qì)屬性 /// </summary> ₽& public class ExceptionH≥φandlingAttribute : ExceptionFilterAttribute ∑→★ { /// <summary> Ω→π≠ /// 統一(yī)對(duì)調用(yòng)異常信 ¥α★息進行(xíng)處理(lǐ),返回自(zì)定義的(de★)異常信息 /// </su∏₩∞↑mmary> /// <param na$♥me="context">HTTP上(shàng)下(xià)文(wén)對(duì)象€↕</param> public override void OnEx♣☆€&ception(HttpActionExecutedC♣≥γontext context) {™$≠ //自(zì)定義異常的(de)處理(lǐ) ≥× MyApiException ex = conteσ♣ xt.Exception as MyApiException; if←≥ (ex != null) { ↔ ₽//記錄關鍵的(de)異常信息 LogHelper.→∑φ∏Error(context.Exception); Ω throw new HttpResponseExcep<≥ε<tion(new HttpResponseMessage(HttpStat ★βusCode.InternalServerError) ™€¥ { ≤σ♠★ //封裝處理(lǐ)異常信息,返回指定JSON對(duì)象 ✔♠£♠ Content = new™♥≠ StringContent(new BaseResultJson(exβσ≈₹.Message, false, ex.errcode).ToJson()), ↕× ReasonPhrase =₹α< "Exception" ¥↑Ω }); } "¶≤≈ /£¶≠/常規異常的(de)處理(lǐ) string msg = s→≤tring.IsNullOrEmpty(context.Exception.Ω₹↓Message) ? "接口出現(xiàn)了(le)錯(cu₹&ò)誤,請(qǐng)重試或者聯系管理(lǐ)員(yuán)" : conte↕> xt.Exception.Message; throw newφ∑λ HttpResponseException(new HttpResponseMessage(H€Ω§¶ttpStatusCode.InternalServerError) Ω { Content =÷↑≈ new StringContent(msg), ≠ ✘ ReasonPhrase = "Critical Exception" ¶♣σ← }); } }
3)小(xiǎo)程序端代碼處理(lǐ)
小(xiǎo)程序端主要(yào)是(shì)通(tōng)過JS代碼± ×↕進行(xíng)處理(lǐ),實現(xiàn)數(shù)據的(de)獲取及提交處理(l↓αǐ)等。
如(rú)我們列舉一(yī)個(gè)代表性的(de)POST<≥₽♥處理(lǐ)代碼,如(rú)下(xià)所示。
//測試POst方法 wx.request({≥♠§ url: 'http://localhost:27206/api/S★שmallApp/Test/TestPostObject', $∑↓§ data: { msg : '測試內(nèi)容', §₩<♣ item: {Text :'Text', Value:'testValue'} ε> }, header: {'Content-Type': 'application/j÷§≥ son' }, method: 'POST', successγ₩: function (res) { console.log(resδ™∞.data); } });
而對(duì)于GET方式,我們的(de)小(xiǎo)程序調用(yòng)方式如(rú)下(x☆↕&ià)所示。
getFilms: function(start) { consol¥$↑ e.log('start:' + start); var that = t• αhis wx.request({ u&↔↕←rl: 'http://www.iqidi.com/api/h5§♦↔♥/test/movies', data: { ≤♠ offset: start, type: 'hot', l★♦™Ωimit: that.data.limit }, ✘↑↔ header: { 'Content-Type': 'appl♦↑ication/json' }, su₩λ§ccess: function (res) { console.log±£•×(res.data) var data = http://www.wxapp€←$-union.com/res.data.data; co $nsole.log(data); if (data.movies.length ♥✘σ=== 0) { that.setData({ β↓ hasMore: false, hiδ↕&deLoading :true, ®₽₹ }) } else≥σ↔ { that.setData({ films"$•: that.data.films.concat(data.movies), ↔↑♠≤ start: that.data.start + da≠ εta.movies.length, α← hasMore: true, 'γ hideLoading :true, βφ§ }); } } & ±¥ })
以上(shàng)就(jiù)是(shì)我們常規接口(單個(gè)參數(shù)或™≥÷≠者多(duō)個(gè)參數(shù),簡單對(duì)象和(hé)複雜(δzá)對(duì)象的(de)處理(lǐ))的(de)∑ 定義代碼,希望讀(dú)者在開(kāi)發Web API接口的(dσe)時(shí)候,可(kě)以有(yǒu)所幫助。