對(duì)于客戶端應(yīng)用程序,免不了和遠(yuǎn)程服務(wù)打交道。設(shè)計(jì)一個(gè)良好的『服務(wù)層』能幫我們規(guī)范和分離業(yè)務(wù)代碼,提高生產(chǎn)效率。服務(wù)層最核心的模塊一定是怎樣發(fā)送請(qǐng)求,雖然Mono提供了很多C#網(wǎng)絡(luò)請(qǐng)求類,諸如WebClient,HttpWebRequest,但考慮到跨平臺(tái),這些類不一定適用。不過(guò)不用擔(dān)心,Unity 5.x提供了新的與網(wǎng)絡(luò)相關(guān)類UnityWebRequest用來(lái)替代原先的WWW,這是官方推薦的,也是最佳選擇。
使用Token進(jìn)行身份驗(yàn)證
首先我們必須要考慮的是,怎樣和Web服務(wù)安全的通信。沒(méi)錯(cuò),肯定是身份驗(yàn)證(Authentication)。對(duì)于像WebClient這些類,它們會(huì)提供一個(gè)屬性,比如Credentials,可以在此屬性設(shè)置一些身份驗(yàn)證信息,比如用戶名,密碼,域。這是一個(gè)很『重』的解決方案,且不論是否能在Unity中實(shí)現(xiàn),單從密碼這個(gè)角度,很多游戲根本不需要密碼。所以,我們需要一種『輕』量級(jí)的身份驗(yàn)證機(jī)制,這就是Token,中文翻譯叫『令牌』。
Token有兩個(gè)重要的特點(diǎn):
代表了唯一的身份驗(yàn)證令牌
具有時(shí)效性
第一點(diǎn)我們肯定可以理解,唯一性是身份驗(yàn)證的的基礎(chǔ)。那第二點(diǎn)怎么理解呢?其實(shí),Token本質(zhì)上是一串加密過(guò)后的字符串,如果沒(méi)有時(shí)效性,萬(wàn)一被竊取之后,他人很容易進(jìn)行偽造。所以,易變的Token一定比不變的安全,你需要一個(gè)算法來(lái)動(dòng)態(tài)生成Token,我提供一個(gè)簡(jiǎn)單的算法:
md5(((day*10) + (month*100) + (last2DigitsofYear)*1000)+userId+deviceId)
同理,你需要在Web服務(wù)前加上一個(gè)過(guò)濾器,一樣的算法來(lái)驗(yàn)證Token是否一致。
Request Pipeline
Pipeline是管道的意思,管道是相連的,代表了請(qǐng)求的流轉(zhuǎn)。由于UnityWebRequest必須配合StartCoroutine,而StartCoroutine又屬于View層的代碼,這和分層(詳見(jiàn)之前的文章)沖突,MVVM框架需要將業(yè)務(wù)邏輯從View解耦。一個(gè)比較好的解決方案是通過(guò)中介的HttpTool來(lái)解決,它是一個(gè)單例的MonoBehaviour,并且不會(huì)隨著場(chǎng)景的加載被銷毀。
public class HttpTool : Singleton
{
// 無(wú)法在外界使用構(gòu)造函數(shù),確保Singleton
protected HttpTool() { }
}
不管是請(qǐng)求還是響應(yīng),本質(zhì)上是一堆數(shù)據(jù)的集合,將這些數(shù)據(jù)封裝成對(duì)象的形式會(huì)更加容易管理,我將請(qǐng)求相關(guān)的數(shù)據(jù)封裝成HttpRequest對(duì)象:
public class HttpRequest
{
public string Url { get; set; }
public HttpMethod Method { get; set; }
public string Parameters { get; set; }
}
而將從Web服務(wù)返回的數(shù)據(jù)封裝成HttpResponse對(duì)象:
public class HttpResponse
{
public bool IsSuccess { get; set; }
public string Error { get; set; }
public long StatusCode { get; set; }
public string Data { get; set; }
}
值得注意的是,對(duì)應(yīng)Http請(qǐng)求,不論Get還是Post都會(huì)將參數(shù)組裝成“field1=value1&field2=value2”格式,不同的是Get請(qǐng)求,參數(shù)會(huì)跟在Url后,而Post請(qǐng)求則在Request Body里。所以需要一個(gè)幫助類,反射要傳遞的對(duì)象屬性,拼裝返回字符串。
核心的請(qǐng)求交由UnityWebRequest實(shí)現(xiàn),通過(guò)yield等待返回的結(jié)果:
using (var www = UnityWebRequest.Get(url + parameters))
{
yield return www.Send();
var response = new HttpResponse
{
IsSuccess = !www.isError, Error = www.error, StatusCode = www.responseCode, Data = www.downloadHandler.text
};
onComplete(response);
}
最后再對(duì)返回的Json字符串反序列化成對(duì)象,值得注意的是,在此我用了內(nèi)置的JsonUtility類,它并不能直接反序列化一個(gè)Json數(shù)組 ,而是需要將它包裝成一個(gè)對(duì)象 ,通過(guò)集合類型屬性的形式間接被反序列化。
至此,一個(gè)完整的Request Pipeline 如下圖所示:

使用策略模式增強(qiáng)RemoteRepository
由于JsonUtility的限制因素多,你可能使用其他第三方的庫(kù)。又或者不反序列化Json,而是Xml。所以在RemoteRepository中不應(yīng)該限制死反序列化的代碼,更好的想法是通過(guò)『策略模式』,交由外部算法來(lái)實(shí)現(xiàn)。這樣的好處是你根本不需要改動(dòng)RemoteRepository里的代碼,這也符合『開(kāi)閉原則』。
所以,你需要在RemoteRepository定義一個(gè)序列化接口:
public ISerializer Serializer { get; set; }
然后,對(duì)返回的HttpResponse中的Json反序列化:
Serializer.Deserialize(httpResponse.Data)
真正的對(duì)Json序列化器實(shí)現(xiàn)了ISerializer接口,以策略的形式存在:
public class SerializerJson:ISerializer
{
public static readonly SerializerJson Instance=new SerializerJson();
private SerializerJson()
{
}
public string Serialize(T obj, bool readableOutput = false) where T : class, new()
{
throw new NotImplementedException();
}
public T Deserialize(string json) where T : class, new()
{
return JsonUtility.FromJson(json);
}
}
策略模式在編程領(lǐng)域運(yùn)用非常廣,比如Java或者.NET框架里的集合排序,大量用到策略模式。由程序員指定的算法來(lái)最終實(shí)現(xiàn)排序。
本文的核心思想就是如何在合理分層結(jié)果下構(gòu)建一個(gè)好用的服務(wù)層。談到了如何動(dòng)態(tài)生成Token來(lái)實(shí)現(xiàn)身份驗(yàn)證,以及分層情況下的請(qǐng)求流程。對(duì)于2D并且以數(shù)據(jù)綁定為基礎(chǔ)的游戲,我認(rèn)為這是一個(gè)好的實(shí)踐方案。因?yàn)椴还苁侨龑蛹軜?gòu)還是N層架構(gòu),通過(guò)分層的好處是更加清晰去實(shí)現(xiàn)業(yè)務(wù)邏輯。
更多關(guān)于unity培訓(xùn)的問(wèn)題,歡迎咨詢千鋒教育在線名師。千鋒教育擁有多年IT培訓(xùn)服務(wù)經(jīng)驗(yàn),采用全程面授高品質(zhì)、高體驗(yàn)培養(yǎng)模式,擁有國(guó)內(nèi)一體化教學(xué)管理及學(xué)員服務(wù),助力更多學(xué)員實(shí)現(xiàn)高薪夢(mèng)想。