基礎(chǔ)篇對(duì)LSYNetworking的基本使用進(jìn)行了說(shuō)明,進(jìn)階篇將講解一下LSYNetworking
對(duì)復(fù)雜業(yè)務(wù)場(chǎng)景的支持粱快,比如大家用的最多的加解密功能秩彤,緩存功能等;
請(qǐng)求參數(shù)加密
如果你需要在請(qǐng)求之前將參數(shù)進(jìn)行加密事哭,那么你需要在你的BaseRequest
里重寫(xiě)handleParams:
這個(gè)方法漫雷,在此方法里進(jìn)行加密工作;
接著上一篇中的例子鳍咱,在YourBaseRequest
中添加一個(gè)屬性needEncrypt
降盹,用來(lái)表示當(dāng)前請(qǐng)求的參數(shù)是否需要加密咧纠,如果你的所有請(qǐng)求都是需要加密的抒抬,也可以不要這個(gè)參數(shù)织中。
下邊是handleParams:
方法的實(shí)現(xiàn):
- (NSDictionary *)handleParams:(NSDictionary *)params{
if (!_needEncrypt) {
return params;
}
NSDictionary *encryptedParams = params.copy;//假裝這是加密操作
return encryptedParams;
}
至此抗悍,加密功能就添加完畢了惦蚊。
需要說(shuō)明一下的是串述,這個(gè)方法是在子線程執(zhí)行的皿淋,這樣設(shè)計(jì)是為了避免加密操作太過(guò)耗時(shí)而占用主線程時(shí)間或听;
返回值解密
服務(wù)器對(duì)請(qǐng)求返回值的加密策略一般有兩種脯倚,一種是對(duì)整個(gè)
response
進(jìn)行加密渔彰,另一種是對(duì)response
的result
部分進(jìn)行加密;
這兩種加密方式推正,除了加密的部分不一樣外恍涂,還有一點(diǎn)需要注意的是,請(qǐng)求的responseType
也是不一樣的植榕。
對(duì)response
整體進(jìn)行加密乳丰,responseType
需要設(shè)置成HTTP
,即返回的數(shù)據(jù)是NSData
類(lèi)型的内贮,要等到我們解密后产园,才會(huì)是json;
而只加密result
夜郁,responseType
需要設(shè)置成JSON
(當(dāng)然什燕,你若設(shè)置成HTTP
也是沒(méi)問(wèn)題的,只不過(guò)這樣需要做額外的工作)竞端。
如果要將返回值解密屎即,需要在handleResponse:
方法中添加解密邏輯。
在YourBaseRequest
中添加兩個(gè)屬性,needDecryptResponse
和needDecryptResult
技俐,分別用來(lái)表示是否需要解密response
和result
乘陪。
然后給YourBaseResponse
添加“用加密的response初始化”和“用result加密的response初始化”這兩個(gè)初始化方法:
- (instancetype)initWithEncryptedResponse:(id)response;
- (instancetype)initWithResultEncryptedResponse:(id)response;
然后在handleResponse:
方法中添加相關(guān)邏輯:
- (id<LSYResponseProtocol>)handleResponse:(id)response{
if (_needDecryptResponse) {
//如果是對(duì)整個(gè)response進(jìn)行加密的情況
//順便一說(shuō),如果是對(duì)整個(gè)response進(jìn)行加密,那么responseType一定需要是http
return [[YourBaseResponse alloc] initWithEncryptedResponse:response];
}else {
//如果是xml的response,需要進(jìn)行xml解析
if ([response isKindOfClass:NSXMLParser.class]) {
response = [NSDictionary dictionaryWithXMLParser:response];
}
if (_needDecryptResult){
//如果是對(duì)result進(jìn)行加密的情況
return [[YourBaseResponse alloc] initWithResultEncryptedResponse:response];
}
//不需要解密
return [[YourBaseResponse alloc] initWithResponse:response];
}
}
handleResponse:
這個(gè)方法也是在子線程執(zhí)行的,不用擔(dān)心解密操作太過(guò)耗時(shí)卡住主線程的問(wèn)題雕擂。
別忘了修改responseType
:
-(LSYResponseSerializerType)responseType{
if (_needDecryptResponse) {
return LSYResponseSerializerTypeHTTP;
}
return LSYResponseSerializerTypeJSON;
}
接下來(lái)是兩個(gè)初始化方法的實(shí)現(xiàn):
- (instancetype)initWithEncryptedResponse:(id)response{
response = [response copy];//假裝這是解密操作
return [self initWithResponse:response];
}
- (instancetype)initWithResultEncryptedResponse:(id)response{
self = [self initWithResponse:response];
//對(duì)result進(jìn)行解密
self.result = [self.result copy];//假裝這是解密操作
return self;
}
返回測(cè)試數(shù)據(jù)
開(kāi)發(fā)過(guò)程中啡邑,經(jīng)常會(huì)遇到,接口信息已經(jīng)和后臺(tái)協(xié)商完畢井赌,我們的UI部分也已經(jīng)開(kāi)發(fā)完畢谤逼,就等接口了,可是接口還沒(méi)好仇穗。
這時(shí)候我們可以先將UI的數(shù)據(jù)寫(xiě)死流部,然后等后端開(kāi)發(fā)完接口,再繼續(xù)開(kāi)發(fā)調(diào)試纹坐。
如果你使用了LSYNetworking
枝冀,你還會(huì)有另一個(gè)選擇,你可以在沒(méi)有接口的情況下耘子,返回測(cè)試數(shù)據(jù)果漾,進(jìn)行UI調(diào)試。
如果返回值的結(jié)構(gòu)拴还,字段名稱(chēng)跨晴,類(lèi)型等,都已經(jīng)和后臺(tái)商量好了片林,那么當(dāng)你用測(cè)試數(shù)據(jù)調(diào)試完畢后端盆,基本就沒(méi)有額外的開(kāi)發(fā)了。
要實(shí)現(xiàn)測(cè)試功能费封,需要重寫(xiě)Request
中的responseDataSource
這個(gè)方法焕妙。假設(shè),這里有個(gè)XXXFriendListRequest
弓摘,用來(lái)獲取好友信息列表焚鹊,類(lèi)我已經(jīng)創(chuàng)建好,需要添加的內(nèi)容也都添加完畢了韧献,但是末患,沒(méi)有接口!
為了測(cè)試锤窑,我創(chuàng)建了一個(gè)plist文件璧针,作為數(shù)據(jù)源:
然后,我重寫(xiě)了XXXFriendListRequest
的responseDataSource
方法:
-(id)responseDataSource{
NSArray *friendList = [NSArray arrayWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"FriendsInfoList" ofType:@"plist"]];
return @{
@"resultcode":@(200),
@"resultmsg":@"請(qǐng)求成功!",
@"result":friendList
};
}
當(dāng)然渊啰,你也可以不要plist文件探橱,直接這么寫(xiě):
-(id)responseDataSource{
return @{
@"resultcode":@(200),
@"resultmsg":@"請(qǐng)求成功!",
@"result":@[
@{
@"user_id" : @0,
@"name" : @"張三",
@"sex" : @0,
@"age" : @22
},
@{
@"user_id" : @1,
@"name" : @"李四",
@"sex" : @1,
@"age" : @20
}
]
};
}
如果返回了responseDataSource
申屹,則會(huì)跳過(guò)請(qǐng)求的步驟,直接使用responseDataSource
作為Response
返回隧膏。
數(shù)據(jù)緩存
所謂緩存哗讥,其實(shí)就是將本次請(qǐng)求的結(jié)果保存在本地,然后在下次請(qǐng)求中胞枕,讀取之前存儲(chǔ)的Response
作為responseDataSource
返回杆煞;
在YourBaseRequest
中添加一個(gè)屬性shouldCache
,用來(lái)表示當(dāng)前請(qǐng)求是否需要緩存曲稼。再添加一個(gè)readonly
的屬性isCachedResponse
索绪,用來(lái)表示當(dāng)前的請(qǐng)求是否使用的緩存湖员。
然后我們需要給YourBaseResponse
添加一個(gè)屬性responseJson
贫悄,用來(lái)存放最原始的json數(shù)據(jù)。
接下來(lái)娘摔,我們要修改YourBaseResponse
的初始化方法窄坦,在初始化的時(shí)候,存儲(chǔ)一下responseJson
數(shù)據(jù):
- (instancetype)initWithResultEncryptedResponse:(id)response{
self = [self initWithResponse:response];
//對(duì)result進(jìn)行解密
self.result = [self.result copy];//假裝這是解密操作
//因?yàn)樯婕暗骄彺鍾esponse,所以需要在result解密后,將Response的result替換為解密后的數(shù)據(jù)
NSMutableDictionary *decryptResponse = [_responseJson mutableCopy];
if (!_result) {
[decryptResponse removeObjectForKey:@"result"];
}else{
[decryptResponse setObject:_result forKey:@"result"];
}
_responseJson = decryptResponse.copy;
return self;
}
- (instancetype)initWithResponse:(id)response{
self = [super init];
if (self) {
_code = [[response objectForKey:@"resultcode"] integerValue];
_msg = [response objectForKey:@"resultmsg"];
_result = [response objectForKey:@"result"];
_responseJson = response;
}
return self;
}
當(dāng)然凳寺,我這里默認(rèn)是緩存解密后的Response
鸭津,如果想要緩存解密前的,可以在YourBaseRequest
的handleResponse:
方法中獲取最原始的Response
肠缨。
已經(jīng)有了response逆趋,接下來(lái),我們需要重寫(xiě)YourBaseRequest
請(qǐng)求成功的方法晒奕,在這個(gè)方法中緩存數(shù)據(jù)(失敗了不需要緩存闻书,如果當(dāng)前已經(jīng)是讀取的緩存也不需要緩存):
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
//可以在這里處理緩存邏輯
YourBaseResponse *res = response;
if(_shouldCache && !_isCachedResponse) {
//假裝這是寫(xiě)入緩存操作
[NSUserDefaults.standardUserDefaults setObject:[res.responseJson yy_modelToJSONString] forKey:self.url];
[NSUserDefaults.standardUserDefaults synchronize];
}
}
然后,在responseDataSource
中讀取緩存并返回:
- (id)responseDataSource{
//可以在這里處理緩存邏輯
if(_shouldCache) {
//假裝這是讀取緩存操作
NSDictionary *cachedResponse = [NSUserDefaults.standardUserDefaults objectForKey:self.url];
//本地是否有已緩存的數(shù)據(jù)
if (cachedResponse) {
_isCachedResponse = YES;
return cachedResponse;
}
}
return nil;
}
如果緩存的是解密后的Response
脑慧,則不需要再解密了魄眉,別忘了在handleResponse:
方法中加個(gè)判斷:
//緩存不需要解密
if (_isCachedResponse) {
return [[YourBaseResponse alloc] initWithResponse:response];
}
自動(dòng)顯示/隱藏loading view
如果我們?cè)诿看握?qǐng)求的時(shí)候,都需要在請(qǐng)求之前顯示loading view闷袒,在請(qǐng)求成功/失敗之后隱藏loading view坑律,那可能確實(shí)比較麻煩,因此囊骤,我們可以添加一個(gè)自動(dòng)轉(zhuǎn)菊花的功能晃择。
我們可以在YourBaseRequest
中添加一個(gè)屬性showLoadingView
,用來(lái)表示是否自動(dòng)轉(zhuǎn)菊花也物,然后我們?cè)?code>YourBaseRequest中重寫(xiě)startRequest
方法宫屠,在此處顯示loading view:
-(NSString *)startRequestWithSuccessBlock:(LSYRequestSuccessBlock)successBlock
failureBlock:(LSYRequestFailBlock)failureBlock{
if (_showLoadingView) {
//這里可以開(kāi)始轉(zhuǎn)菊花
}
return [super startRequestWithSuccessBlock:successBlock failureBlock:failureBlock];
}
然后重寫(xiě)requestSuccess
和requestFailed
方法,隱藏loading view:
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
if (_showLoadingView) {
//這里可以取消轉(zhuǎn)菊花
}
}
- (void)requestFailedWithError:(NSError *)error task:(nonnull NSURLSessionTask *)task successBlock:(LSYRequestSuccessBlock _Nullable)successBlock failureBlock:(LSYRequestFailBlock _Nullable)failureBlock{
if (_showLoadingView) {
//這里可以取消轉(zhuǎn)菊花
}
}
這樣焦除,我們就可以在子類(lèi)的init
方法里進(jìn)行設(shè)置激况,這個(gè)子類(lèi)默認(rèn)就會(huì)轉(zhuǎn)菊花。
統(tǒng)一處理錯(cuò)誤信息
可以在YourBaseRequest
的requestFailed
方法中處理一些公共的錯(cuò)誤信息,如未登錄乌逐,安全驗(yàn)證竭讳,Alert提示等:
- (void)requestFailedWithError:(NSError *)error task:(nonnull NSURLSessionTask *)task successBlock:(LSYRequestSuccessBlock _Nullable)successBlock failureBlock:(LSYRequestFailBlock _Nullable)failureBlock{
//處理一些公共的錯(cuò)誤
if (error.isBusinessError) {
//處理業(yè)務(wù)邏輯上的錯(cuò)誤
}else{
//處理其他錯(cuò)誤
}
}
對(duì)某個(gè)請(qǐng)求返回值的特殊處理
舉個(gè)例子,XXXFriendListRequest
返回的好友列表浙踢,需要根據(jù)年齡進(jìn)行排序展示绢慢。
我們可以在successBlock
的回調(diào)里做這件事,但如果這個(gè)請(qǐng)求在多個(gè)地方調(diào)用呢洛波?我們總不能每次都要寫(xiě)一遍排序代碼胰舆。雖然我們可以專(zhuān)門(mén)寫(xiě)一個(gè)Util
對(duì)好友信息進(jìn)行排序,但這不夠好蹬挤。
最合適的當(dāng)然是缚窿,請(qǐng)求最終返回的數(shù)據(jù)就是排過(guò)序的,所以我們可以重寫(xiě)XXXFriendListRequest
的requestSuccess
方法:
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
NSArray<XXXFirendInfo *> *firendList = response.result;
//這里可以對(duì)firendList進(jìn)行一些處理,如排序操作,寫(xiě)在這里可以避免每次請(qǐng)求都需要在請(qǐng)求回調(diào)里寫(xiě)相同的排序邏輯
response.result = firendList.copy;//假設(shè)這是排序操作
}
當(dāng)然焰扳,我們也可以修改返回值的內(nèi)容倦零,用來(lái)測(cè)試。
更多功能
可以在YourBaseRequest
的requestSuccess
和requestFailed
方法中吨悍,處理更多邏輯扫茅,比如實(shí)現(xiàn)一些網(wǎng)絡(luò)請(qǐng)求信息記錄,網(wǎng)絡(luò)請(qǐng)求埋點(diǎn)等debug功能。
小結(jié)
至此育瓜,LSYNetworking
的使用說(shuō)明就全部講完了~我所遇到的葫隙,想到的功能都在這里了。
當(dāng)然躏仇,LSYNetworking
可拓展的功能不止這些恋脚,我相信,它可以處理更復(fù)雜的業(yè)務(wù)邏輯钙态,任何屬于網(wǎng)絡(luò)框架的的業(yè)務(wù)慧起,應(yīng)該都能找到一個(gè)合適的節(jié)點(diǎn)進(jìn)行添加,如果沒(méi)有找到册倒,希望你能告訴我蚓挤,我會(huì)繼續(xù)優(yōu)化這個(gè)框架~
大家感興趣的話,可以下載一下我寫(xiě)的 Demo驻子,里邊有一些詳細(xì)的代碼實(shí)現(xiàn)~