基础篇对LSYNetworking的基本使用进行了说明,进阶篇将讲解一下LSYNetworking
对复杂业务场景的支持,比如大家用的最多的加解密功能,缓存功能等;
请求参数加密
如果你需要在请求之前将参数进行加密,那么你需要在你的BaseRequest
里重写handleParams:
这个方法,在此方法里进行加密工作;
接着上一篇中的例子,在YourBaseRequest
中添加一个属性needEncrypt
,用来表示当前请求的参数是否需要加密,如果你的所有请求都是需要加密的,也可以不要这个参数。
下边是handleParams:
方法的实现:
- (NSDictionary *)handleParams:(NSDictionary *)params{
if (!_needEncrypt) {
return params;
}
NSDictionary *encryptedParams = params.copy;//假装这是加密操作
return encryptedParams;
}
至此,加密功能就添加完毕了。
需要说明一下的是,这个方法是在子线程执行的,这样设计是为了避免加密操作太过耗时而占用主线程时间;
返回值解密
服务器对请求返回值的加密策略一般有两种,一种是对整个
response
进行加密,另一种是对response
的result
部分进行加密;
这两种加密方式,除了加密的部分不一样外,还有一点需要注意的是,请求的responseType
也是不一样的。
对response
整体进行加密,responseType
需要设置成HTTP
,即返回的数据是NSData
类型的,要等到我们解密后,才会是json;
而只加密result
,responseType
需要设置成JSON
(当然,你若设置成HTTP
也是没问题的,只不过这样需要做额外的工作)。
如果要将返回值解密,需要在handleResponse:
方法中添加解密逻辑。
在YourBaseRequest
中添加两个属性,needDecryptResponse
和needDecryptResult
,分别用来表示是否需要解密response
和result
。
然后给YourBaseResponse
添加“用加密的response初始化”和“用result加密的response初始化”这两个初始化方法:
- (instancetype)initWithEncryptedResponse:(id)response;
- (instancetype)initWithResultEncryptedResponse:(id)response;
然后在handleResponse:
方法中添加相关逻辑:
- (id<LSYResponseProtocol>)handleResponse:(id)response{
if (_needDecryptResponse) {
//如果是对整个response进行加密的情况
//顺便一说,如果是对整个response进行加密,那么responseType一定需要是http
return [[YourBaseResponse alloc] initWithEncryptedResponse:response];
}else {
//如果是xml的response,需要进行xml解析
if ([response isKindOfClass:NSXMLParser.class]) {
response = [NSDictionary dictionaryWithXMLParser:response];
}
if (_needDecryptResult){
//如果是对result进行加密的情况
return [[YourBaseResponse alloc] initWithResultEncryptedResponse:response];
}
//不需要解密
return [[YourBaseResponse alloc] initWithResponse:response];
}
}
handleResponse:
这个方法也是在子线程执行的,不用担心解密操作太过耗时卡住主线程的问题。
别忘了修改responseType
:
-(LSYResponseSerializerType)responseType{
if (_needDecryptResponse) {
return LSYResponseSerializerTypeHTTP;
}
return LSYResponseSerializerTypeJSON;
}
接下来是两个初始化方法的实现:
- (instancetype)initWithEncryptedResponse:(id)response{
response = [response copy];//假装这是解密操作
return [self initWithResponse:response];
}
- (instancetype)initWithResultEncryptedResponse:(id)response{
self = [self initWithResponse:response];
//对result进行解密
self.result = [self.result copy];//假装这是解密操作
return self;
}
返回测试数据
开发过程中,经常会遇到,接口信息已经和后台协商完毕,我们的UI部分也已经开发完毕,就等接口了,可是接口还没好。
这时候我们可以先将UI的数据写死,然后等后端开发完接口,再继续开发调试。
如果你使用了LSYNetworking
,你还会有另一个选择,你可以在没有接口的情况下,返回测试数据,进行UI调试。
如果返回值的结构,字段名称,类型等,都已经和后台商量好了,那么当你用测试数据调试完毕后,基本就没有额外的开发了。
要实现测试功能,需要重写Request
中的responseDataSource
这个方法。假设,这里有个XXXFriendListRequest
,用来获取好友信息列表,类我已经创建好,需要添加的内容也都添加完毕了,但是,没有接口!
为了测试,我创建了一个plist文件,作为数据源:
测试用的plist文件
然后,我重写了XXXFriendListRequest
的responseDataSource
方法:
-(id)responseDataSource{
NSArray *friendList = [NSArray arrayWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"FriendsInfoList" ofType:@"plist"]];
return @{
@"resultcode":@(200),
@"resultmsg":@"请求成功!",
@"result":friendList
};
}
当然,你也可以不要plist文件,直接这么写:
-(id)responseDataSource{
return @{
@"resultcode":@(200),
@"resultmsg":@"请求成功!",
@"result":@[
@{
@"user_id" : @0,
@"name" : @"张三",
@"sex" : @0,
@"age" : @22
},
@{
@"user_id" : @1,
@"name" : @"李四",
@"sex" : @1,
@"age" : @20
}
]
};
}
如果返回了responseDataSource
,则会跳过请求的步骤,直接使用responseDataSource
作为Response
返回。
数据缓存
所谓缓存,其实就是将本次请求的结果保存在本地,然后在下次请求中,读取之前存储的Response
作为responseDataSource
返回;
在YourBaseRequest
中添加一个属性shouldCache
,用来表示当前请求是否需要缓存。再添加一个readonly
的属性isCachedResponse
,用来表示当前的请求是否使用的缓存。
然后我们需要给YourBaseResponse
添加一个属性responseJson
,用来存放最原始的json数据。
接下来,我们要修改YourBaseResponse
的初始化方法,在初始化的时候,存储一下responseJson
数据:
- (instancetype)initWithResultEncryptedResponse:(id)response{
self = [self initWithResponse:response];
//对result进行解密
self.result = [self.result copy];//假装这是解密操作
//因为涉及到缓存Response,所以需要在result解密后,将Response的result替换为解密后的数据
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;
}
当然,我这里默认是缓存解密后的Response
,如果想要缓存解密前的,可以在YourBaseRequest
的handleResponse:
方法中获取最原始的Response
。
已经有了response,接下来,我们需要重写YourBaseRequest
请求成功的方法,在这个方法中缓存数据(失败了不需要缓存,如果当前已经是读取的缓存也不需要缓存):
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
//可以在这里处理缓存逻辑
YourBaseResponse *res = response;
if(_shouldCache && !_isCachedResponse) {
//假装这是写入缓存操作
[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];
//本地是否有已缓存的数据
if (cachedResponse) {
_isCachedResponse = YES;
return cachedResponse;
}
}
return nil;
}
如果缓存的是解密后的Response
,则不需要再解密了,别忘了在handleResponse:
方法中加个判断:
//缓存不需要解密
if (_isCachedResponse) {
return [[YourBaseResponse alloc] initWithResponse:response];
}
自动显示/隐藏loading view
如果我们在每次请求的时候,都需要在请求之前显示loading view,在请求成功/失败之后隐藏loading view,那可能确实比较麻烦,因此,我们可以添加一个自动转菊花的功能。
我们可以在YourBaseRequest
中添加一个属性showLoadingView
,用来表示是否自动转菊花,然后我们在YourBaseRequest
中重写startRequest
方法,在此处显示loading view:
-(NSString *)startRequestWithSuccessBlock:(LSYRequestSuccessBlock)successBlock
failureBlock:(LSYRequestFailBlock)failureBlock{
if (_showLoadingView) {
//这里可以开始转菊花
}
return [super startRequestWithSuccessBlock:successBlock failureBlock:failureBlock];
}
然后重写requestSuccess
和requestFailed
方法,隐藏loading view:
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
if (_showLoadingView) {
//这里可以取消转菊花
}
}
- (void)requestFailedWithError:(NSError *)error task:(nonnull NSURLSessionTask *)task successBlock:(LSYRequestSuccessBlock _Nullable)successBlock failureBlock:(LSYRequestFailBlock _Nullable)failureBlock{
if (_showLoadingView) {
//这里可以取消转菊花
}
}
这样,我们就可以在子类的init
方法里进行设置,这个子类默认就会转菊花。
统一处理错误信息
可以在YourBaseRequest
的requestFailed
方法中处理一些公共的错误信息,如未登录,安全验证,Alert提示等:
- (void)requestFailedWithError:(NSError *)error task:(nonnull NSURLSessionTask *)task successBlock:(LSYRequestSuccessBlock _Nullable)successBlock failureBlock:(LSYRequestFailBlock _Nullable)failureBlock{
//处理一些公共的错误
if (error.isBusinessError) {
//处理业务逻辑上的错误
}else{
//处理其他错误
}
}
对某个请求返回值的特殊处理
举个例子,XXXFriendListRequest
返回的好友列表,需要根据年龄进行排序展示。
我们可以在successBlock
的回调里做这件事,但如果这个请求在多个地方调用呢?我们总不能每次都要写一遍排序代码。虽然我们可以专门写一个Util
对好友信息进行排序,但这不够好。
最合适的当然是,请求最终返回的数据就是排过序的,所以我们可以重写XXXFriendListRequest
的requestSuccess
方法:
- (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
NSArray<XXXFirendInfo *> *firendList = response.result;
//这里可以对firendList进行一些处理,如排序操作,写在这里可以避免每次请求都需要在请求回调里写相同的排序逻辑
response.result = firendList.copy;//假设这是排序操作
}
当然,我们也可以修改返回值的内容,用来测试。
更多功能
可以在YourBaseRequest
的requestSuccess
和requestFailed
方法中,处理更多逻辑,比如实现一些网络请求信息记录,网络请求埋点等debug功能。
小结
至此,LSYNetworking
的使用说明就全部讲完了~我所遇到的,想到的功能都在这里了。
当然,LSYNetworking
可拓展的功能不止这些,我相信,它可以处理更复杂的业务逻辑,任何属于网络框架的的业务,应该都能找到一个合适的节点进行添加,如果没有找到,希望你能告诉我,我会继续优化这个框架~
大家感兴趣的话,可以下载一下我写的 Demo,里边有一些详细的代码实现~
网友评论