很多開發(fā) APP 的童鞋還只是會(huì)在 Android Studio 上寫一些 Java 代碼栋猖,對(duì) API 接口,服務(wù)之類的知之甚少,也對(duì) HTML/CSS/JS 一片朦朧,我覺得這是不好的火惊,擴(kuò)大知識(shí)面還是很有必要的,就算不能解決實(shí)際問題奔垦,有時(shí)候會(huì)提供一種解決問題的思路屹耐,比如某個(gè)產(chǎn)品老板要你一個(gè)月上線,怎么辦呢椿猎。原生代碼一行一行不知道要寫到什么時(shí)候去了惶岭,公司 UI,前端鸵贬,JS工程師一個(gè)都調(diào)動(dòng)不起來。相反如果了解快速開發(fā)技巧脖捻,混合開發(fā)之類的技術(shù)阔逼,就能減少很多工作量了。
我一直喜歡用 .NET 作為后端接口開發(fā)語言地沮,因?yàn)?.NET 開發(fā)效率確實(shí)非常高嗜浮,標(biāo)準(zhǔn)庫也非常成熟健全,不至于隨便什么都要第三方的摩疑,用它主要看中的就是在開發(fā)效率和工業(yè)標(biāo)準(zhǔn)能夠兩者都能兼顧的特點(diǎn)危融,我覺得還行。
很多人一打開 Visual Studio 就已經(jīng)啥了雷袋,創(chuàng)建項(xiàng)目都不知道點(diǎn)哪個(gè)吉殃,也不知道該創(chuàng)建什么類型的項(xiàng)目。是醬紫的,目前微軟主要是推 .NET Core 和 .NET Framework 的蛋勺,.NET Framework 是只能運(yùn)行在 Windows 平臺(tái)上的瓦灶,雖然在 Linux 上有 Mono Runtime 支持 .NET 程序的運(yùn)行,但是運(yùn)行效率比 .NET 在 Windows 上的還是有些差距抱完。.NET Core 是微軟推出的下一代編程框架贼陶,在1.X 版本的時(shí)候,只能支持開發(fā) Web 和 CLI 程序巧娱,不過如今可以開發(fā) WPF 桌面程序了碉怔,雖然目前還只能在 Windows 上運(yùn)行,但是誰知道以后會(huì)不會(huì)在 Linux 上也實(shí)現(xiàn)了桌面 GUI 環(huán)境呢禁添。
.NET Core 很好撮胧,網(wǎng)上教程也很多,這個(gè)是灰常建議去學(xué)習(xí)的上荡,不過我還是比較習(xí)慣用 ASP.NET MVC 和 WebForm 方式趴樱,當(dāng)然拖控件是不可能拖控件了,這輩子都不可能拖控件酪捡,就是用用一般處理程序?qū)憣懡涌谌鳎拍芫S持得了生活的樣子。
既然是服務(wù)接口逛薇,那就意味著沒有什么界面捺疼,或者及少量的界面,用一般處理程序是墜吼的永罚。
創(chuàng)建一個(gè) ASP.NET Web 應(yīng)用程序 (.NET Framework)啤呼,然后選擇空項(xiàng)目,下一步就可以了呢袱。
接下來需要?jiǎng)?chuàng)建一個(gè) HttpHandler 的實(shí)現(xiàn)類官扣,并稍作封裝,讓它成為我們接口的基礎(chǔ)類羞福,因?yàn)橹苯邮褂?HttpHandler 是不那么友好的惕蹄,目前不需要了解為什么要這么做。
如果是為了考慮穩(wěn)定性治专,不那么在乎性能卖陵,可以將整個(gè) ProcessRequest 都用 Try Catch 包裹,避免因?yàn)榻涌谥械腻e(cuò)誤张峰,導(dǎo)致 APP 未能處理正確的返回而導(dǎo)致閃退泪蔫,雖然這種辦法很挫,但是我很喜歡用喘批。
public void ProcessRequest(HttpContext context)
{
if (context.Request.RequestType != "POST")
{
context.Response.StatusCode = 405;
return;
}
AllowCrossDomain = true;
TryExecute.Execute(() =>
{
Context = context;
ProcessUserRequest();
}, error =>
{
bool processed = false;
if (error is SilverStarAmsException)
{
var serror = error as SilverStarAmsException;
if (serror != null)
{
ResponseAsErrorJson(serror.ErrorCode, serror.Message);
processed = true;
}
}
if (!processed)
{
ResponseAsErrorJson(-11, "process api request error");
}
});
}
其次撩荣,API 接口或許會(huì)被小程序或其它服務(wù)用到铣揉,我們對(duì)其做一個(gè)跨域的測(cè)試,在 Header 頭輸出對(duì)應(yīng)的消息婿滓,客戶端即可實(shí)現(xiàn)跨域請(qǐng)求老速,這時(shí)的客戶端通常是瀏覽器,因?yàn)闉g覽器為了安全性會(huì)設(shè)置同源策略凸主,如果接口不支持跨域橘券,接口就不能被使用。
在 ASP.NET 里設(shè)置 Header 有很多種方法卿吐,為了適應(yīng) IIS 的經(jīng)典和集成模式旁舰,以及或許有其他 Web 容器的支持,我都會(huì)寫一遍嗡官,哪種支持就是用哪種箭窜,然后就有了下面的奇怪的代碼:
protected void AppenHeader(string key, string value)
{
bool done = false;
try
{
Context.Response.AppendHeader(key, value);
done = true;
}
catch
{
}
if (!done)
{
try
{
Context.Response.Headers.Add(key, value);
done = true;
}
catch
{
}
}
if (!done)
{
try
{
Context.Response.AddHeader(key, value);
done = true;
}
catch
{
}
}
}
對(duì)于 APP 接口來說穴张,通常是不會(huì)或者對(duì)于安全性很低的情況下才會(huì)用到 GET 請(qǐng)求毒坛,當(dāng)然合服 Restful 規(guī)范的可能也會(huì)用的多,比如訂單的增刪改查:
PUT /api/order
DELETE /api/order/12
PATCH /api/order/12
GET /api/order/12
如果覺得不需要這么多的 HTTP 謂詞磅网,只用 POST 也是可以的婆咸。
我上家公司的 APP 接口全部是這種實(shí)現(xiàn)竹捉,好吧,其實(shí)都是我實(shí)現(xiàn)的尚骄,雖然只有數(shù)十萬用戶块差,但是目前來看,這種架構(gòu)還能支撐倔丈,而且這也不是性能的瓶頸憨闰,只能說在可維護(hù)性上斷然沒有 Restful 方式的好。
接下來我需要建立一個(gè)數(shù)據(jù)庫需五,繼續(xù)是 .NET 當(dāng)然配合 MSSQL 是墜吼的了鹉动,設(shè)計(jì)數(shù)據(jù)庫和表此處略去不表,ORM 我是用 EntityFramework 6.x 宏邮。
以一個(gè)簡(jiǎn)單例子泽示,在數(shù)據(jù)庫中創(chuàng)建了一個(gè) Category 表,表示文章或者某種物體的分類蜀铲,現(xiàn)在要在 APP 里獲取 Category 列表边琉,每頁顯示 20 條數(shù)據(jù)属百,或者由 APP 指定參數(shù)记劝。
創(chuàng)建一個(gè)一般處理程序,GetCategory.ashx 族扰,繼承剛才上面的自定義的 Handler厌丑,重寫 ProcessUserRequest 接口定欧。
聲明三個(gè)參數(shù),表示總頁數(shù)怒竿,當(dāng)前頁碼砍鸠,分頁大小:
int page, pageSize, totalPage;
對(duì)于數(shù)據(jù)庫的訪問耕驰,其實(shí)超簡(jiǎn)單爷辱,看我怎么查出所有數(shù)據(jù):
totalPage = (int)(Math.Ceiling((double)all.Count() / pageSize));
if (page > totalPage) page = totalPage;
if (page < 1) page = 1;
var data = all.OrderByDescending(s => s.CategoryLastChangeDate).Skip((page - 1) * pageSize).Take(pageSize).ToList();
然后輸出查詢出當(dāng)前頁的數(shù)據(jù),并輸出到 JSON朦肘,對(duì)象轉(zhuǎn)為 JSON 饭弓,并不能保證是 100% 成功的,對(duì)于 APP 的接口服務(wù)來說媒抠,穩(wěn)定性最重要弟断,就算是接口出錯(cuò),也必須得返回出錯(cuò)代碼趴生,所以前面的 Handler 的子類有一個(gè)超大的 Try 阀趴,對(duì)于 JSON 輸出,也應(yīng)如此苍匆。
protected void ResponseAsJson(object data)
{
string json = "[]";
TryExecute.Execute(() =>
{
json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
}, error =>
{
json = GetGenericErrorJson(-10, "serialize data error");
});
ResponseAsJsonString(json);
}
完整的接口代碼如下:
/// <summary>
/// GetCategory 的摘要說明
/// </summary>
public class GetCategory : Core.SilverCoreHandlerBase
{
public override void ProcessUserRequest()
{
base.ProcessUserRequest();
int page, pageSize, totalPage;
page = (Context.Request["page"] + "").AsInt(0);
pageSize = (Context.Request["page_size"] + "").AsInt(0);
if (page < 1) page = 1;
if (pageSize < 1 || pageSize > 50) pageSize = 50;
var db = new Data.SilverStarDB();
var all = db.KoteiAssetCategory;
totalPage = (int)(Math.Ceiling((double)all.Count() / pageSize));
if (page > totalPage) page = totalPage;
if (page < 1) page = 1;
var data = all.OrderByDescending(s => s.CategoryLastChangeDate).Skip((page - 1) * pageSize).Take(pageSize).ToList();
ResponseAsJson(new { page = page, total_page = totalPage, data = data });
}
}
F5 執(zhí)行看看刘急,會(huì)提示不支持的謂詞,當(dāng)然了锉桑,只支持 POST 的芭琶埂!可以用 POSTMAN 工具調(diào)試民轴。
好了攻柠,看看最終輸出的 JSON 結(jié)果:
如此,在 APP 中使用相關(guān)的 HTTP 庫后裸,就可以完成接口的調(diào)用了瑰钮,敲你嗎簡(jiǎn)單!