美文网首页iOS点点滴滴
LSYNetworking:基于AFNetworking的网络请

LSYNetworking:基于AFNetworking的网络请

作者: 樂幽 | 来源:发表于2023-06-20 19:41 被阅读0次

    基础篇LSYNetworking的基本使用进行了说明,进阶篇将讲解一下LSYNetworking对复杂业务场景的支持,比如大家用的最多的加解密功能,缓存功能等;

    请求参数加密


    如果你需要在请求之前将参数进行加密,那么你需要在你的BaseRequest里重写handleParams:这个方法,在此方法里进行加密工作;

    接着上一篇中的例子,在YourBaseRequest中添加一个属性needEncrypt,用来表示当前请求的参数是否需要加密,如果你的所有请求都是需要加密的,也可以不要这个参数。

    下边是handleParams:方法的实现:

    - (NSDictionary *)handleParams:(NSDictionary *)params{
        if (!_needEncrypt) {
            return params;
        }
        NSDictionary *encryptedParams = params.copy;//假装这是加密操作
        return encryptedParams;
    }
    

    至此,加密功能就添加完毕了。

    需要说明一下的是,这个方法是在子线程执行的,这样设计是为了避免加密操作太过耗时而占用主线程时间;

    返回值解密


    服务器对请求返回值的加密策略一般有两种,一种是对整个response进行加密,另一种是对responseresult部分进行加密;
    这两种加密方式,除了加密的部分不一样外,还有一点需要注意的是,请求的responseType也是不一样的。
    response整体进行加密,responseType需要设置成HTTP,即返回的数据是NSData类型的,要等到我们解密后,才会是json;
    而只加密resultresponseType需要设置成JSON(当然,你若设置成HTTP也是没问题的,只不过这样需要做额外的工作)。

    如果要将返回值解密,需要在handleResponse:方法中添加解密逻辑。

    YourBaseRequest中添加两个属性,needDecryptResponseneedDecryptResult,分别用来表示是否需要解密responseresult

    然后给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文件

    然后,我重写了XXXFriendListRequestresponseDataSource方法:

    -(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,如果想要缓存解密前的,可以在YourBaseRequesthandleResponse:方法中获取最原始的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];
    }
    

    然后重写requestSuccessrequestFailed方法,隐藏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方法里进行设置,这个子类默认就会转菊花。

    统一处理错误信息


    可以在YourBaseRequestrequestFailed方法中处理一些公共的错误信息,如未登录,安全验证,Alert提示等:

    - (void)requestFailedWithError:(NSError *)error task:(nonnull NSURLSessionTask *)task successBlock:(LSYRequestSuccessBlock _Nullable)successBlock failureBlock:(LSYRequestFailBlock _Nullable)failureBlock{
        //处理一些公共的错误
        if (error.isBusinessError) {
            //处理业务逻辑上的错误
        }else{
            //处理其他错误
        }
    }
    

    对某个请求返回值的特殊处理


    举个例子,XXXFriendListRequest返回的好友列表,需要根据年龄进行排序展示。

    我们可以在successBlock的回调里做这件事,但如果这个请求在多个地方调用呢?我们总不能每次都要写一遍排序代码。虽然我们可以专门写一个Util对好友信息进行排序,但这不够好。

    最合适的当然是,请求最终返回的数据就是排过序的,所以我们可以重写XXXFriendListRequestrequestSuccess方法:

    - (void)requestSuccessWithResponseObject:(id<LSYResponseProtocol>)response task:(nonnull NSURLSessionTask *)task{
        NSArray<XXXFirendInfo *> *firendList = response.result;
        //这里可以对firendList进行一些处理,如排序操作,写在这里可以避免每次请求都需要在请求回调里写相同的排序逻辑
        response.result = firendList.copy;//假设这是排序操作
    }
    

    当然,我们也可以修改返回值的内容,用来测试。

    更多功能


    可以在YourBaseRequestrequestSuccessrequestFailed方法中,处理更多逻辑,比如实现一些网络请求信息记录,网络请求埋点等debug功能。

    小结


    至此,LSYNetworking的使用说明就全部讲完了~我所遇到的,想到的功能都在这里了。

    当然,LSYNetworking可拓展的功能不止这些,我相信,它可以处理更复杂的业务逻辑,任何属于网络框架的的业务,应该都能找到一个合适的节点进行添加,如果没有找到,希望你能告诉我,我会继续优化这个框架~

    大家感兴趣的话,可以下载一下我写的 Demo,里边有一些详细的代码实现~

    相关文章

      网友评论

        本文标题:LSYNetworking:基于AFNetworking的网络请

        本文链接:https://www.haomeiwen.com/subject/amyyydtx.html