自定义网络组件说明

1.前言

 网络层在一个App中,是很重要的部分。苹果对网络请求部分已经做了很好的封装,业界在这基础上,也提供了一些相应封装,如AFNetworkingASIHttpRequestMKNetworkKit等。其中ASIHttpRequestMKNetworkKit,近两年官方已经没有进行相应更新。因此在实际的App开发中,AFNetworking已经成为了事实上各大App的标准配置。

2.AFNetworking 3.0简介

2.1AFURLRequestSerialization

AFURLRequestSerialization的作用主要有两点:

  • 创建NSMutableURLRequest;
  • 封装好所需要的参数到NSMutableURLRequest中。

在实际开发中,我们需要做的只是选择一个满足自己需要的AFURLRequestSerialization。例如:

- (AFHTTPRequestSerializer *)requestSerializerForRequest:(HDFBaseRequest *)request {
    AFHTTPRequestSerializer *requestSerializer = nil;
    if (request.requestSerializerType == HDFRequestSerializerTypeHTTP) {
        requestSerializer = [AFHTTPRequestSerializer serializer];
    } else if (request.requestSerializerType == HDFRequestSerializerTypeJSON) {
        requestSerializer = [AFJSONRequestSerializer serializer];
    }
    
    requestSerializer.timeoutInterval = [request requestTimeoutInterval];
    [requestSerializer setValue:@"gof_app/1.0" forHTTPHeaderField:@"User-Agent"];
    
    //请求认证的用户名和密码
    NSArray<NSString *> *authorizationHeaderFieldArray = [request requestAuthorizationHeaderFieldArray];
    if (authorizationHeaderFieldArray != nil) {
        //在http 头里面添加用户名和密码验证
        [requestSerializer setAuthorizationHeaderFieldWithUsername:authorizationHeaderFieldArray.firstObject
                                                          password:authorizationHeaderFieldArray.lastObject];
    }
    
    //附加的请求Header字段
    NSDictionary<NSString *, NSString *> *headerFieldValueDictionary = [request requestHeaderFieldValueDictionary];
    if (headerFieldValueDictionary != nil) {
        for (NSString *httpHeaderField in headerFieldValueDictionary.allKeys) {
            NSString *value = headerFieldValueDictionary[httpHeaderField];
            [requestSerializer setValue:value forHTTPHeaderField:httpHeaderField];
        }
    }
    return requestSerializer;
}

下面我们先看看它的类图,根据类图来进一步了解。

对于上面的类图,我们来做一个简单的讲解:

  • AFURLRequestSerialization协议:定义了一个方法,需要各个子类去实现。
/**
 Returns a request with the specified parameters encoded into a copy of the original request.

 @param request The original request.
 @param parameters The parameters to be encoded.
 @param error The error that occurred while attempting to encode the request parameters.

 @return A serialized request.
 */
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
  • AFHTTPRequestSerializer、AFJSONRequestSerializer、AFPropertyListRequestSerializer:具体的实现类,用户进行网络请求时可根据自身需求选择这三个Serializer其中之一。
序号
请求方式
结论
1
get、head、delete
调用了AFHTTPRequestSerializer的序列化方法, 把parameters拼结成URL字符串参数加到URL后面作为请求的一部分
2
multipart表单提交
在这里面就会用到AFStreamingMultipartFormData,此时parameters及通过AFStreamingMultipartFormData添
加进来的数据都会变一项项的AFHTTPBodyPart存起来,当URL系统需要上传数据时就通过AFHTTPBodyPart取数据。例如文件上传的时候
3 put、post、patch
AFHTTPRequestSerializer:把parameters拼接成字符串参数后序列化成NSData放到HTTPRequest的HTTPBody中。
AFJSONRequestSerializer:把parameters以dataWithJSONObject方式序列化成NSData放到HTTPRequest的HTTPBody中。
AFPropertyListRequestSerializer:把请求parameters以dataWithPropertyList方式序列化成NSData放到HTTPRequest的HTTPBody中。
  • AFMultipartFormData协议:定义了一些接口方法,允许用户可以用不同的方式添加表单的内容,如:使用文件路径、直接用NSData、或使用inputStream等。
  • AFStreamingMultipartFormData:遵循了AFMultipartFormData协议,把协议的方法都实现了。
  • AFMultipartBodyStream:它起着一个重要桥梁作用,上传表单数据时系统会先调到它,然后它会依赖AFHTTPBodyPart读到数据,然后把数据返回给URL系统。
  • AFHTTPBodyPart:每一个AFHTTPBodyPart就是代表一项表单数据,由它真正读取它内部的数据。

2.2AFURLResponseSerialization

AFURLResponseSerialization的主要作用是为我们请求所得到的数据提供验证并进行反序列化,我们只需要按自己的需求选择合适的AFURLResponseSerialization即可,反序列成功后就能得我们想要的结果。

下面是AFURLResponseSerialization类图:

AFURLResponseSerialization协议:定义了一个方法,需要各个子类去实现:

/**
 The response object decoded from the data associated with a specified response.

 @param response The response to be processed.
 @param data The response data to be decoded.
 @param error The error that occurred while attempting to decode the response data.

 @return The object decoded from the specified response data.
 */
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
                           data:(nullable NSData *)data
                          error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
  • AFHTTPResponseSerializer:其它各个子类的基类。

  • AFJSONResponseSerializer:JSON

  • AFXMLParserResponseSerializer:XML,只能返回XMLParser
  • AFPropertyListResponseSerializer:PList

  • AFImageResponseSerializer:Image

  • AFCompoundResponseSerializer:组合

2.3AFSecurityPolicy

AFSecurityPolicy是AFNetworking安全相关的模块,上次HTTPS项目就使用了它。对数字证书感兴趣的,可以看看这篇文章:http://www.360doc.com/content/13/0809/14/1073512_305848184.shtml

在AFSecurityPolicy中,提供了3种验证模式:

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,
    AFSSLPinningModeCertificate,
};
  • AFSSLPinningModeNone:这个模式表示不做SSL pinning,只跟浏览器一样在系统的信任机构列表里验证服务端返回的证书。若证书是信任机构签发的就会通过,若是自己服务器生成的证书,这里是不会通过的。
  •  AFSSLPinningModePublicKey:这个模式同样是用证书绑定方式验证,客户端要有服务端的证书拷贝,只是验证时只验证证书里的公钥,不验证证书的有效期等信息。只要公钥是正确的,就能保证通信不会被窃听,因为中间人没有私钥,无法解开通过公钥加密的数据。
  • AFSSLPinningModeCertificate:这个模式表示用证书绑定方式验证证书,需要客户端保存有服务端的证书拷贝,这里验证分两步:第一步验证证书的域名/有效期等信息;第二步是对比服务端返回的证书跟客户端返回的是否一致。
以上三种模式的逻辑都在 - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain;方法中实现了,感兴趣的可以看看。对于如何获取证书,获取公钥,这些都有系统接口实现。

【备注】:一般来说,每个版本的iOS设备中,都会包含一些既有的CA根证书。如果接收到的证书是iOS信任的CA根证书签名的,那么则为合法证书;否则则为“非法”证书。

2.4AFHTTPSessionManager

通过前面三小块的讲解,大家可能对AFNetworking的工作模式还是一头雾水,不知道是怎么一回事,先别慌,我们慢慢唠。下面我们来看一下AFNetworking的工作流程概图:

从上图可以看出,整个网络请求的核心是AFHTTPSessionManager。和前面一样,我们先来看一下相关类图:

从上面的类图可以看到,AFHTTPSessionManager集成自AFURLSessionManager,那么这两个类都是干什么用的呢?

2.4.1AFURLSessionManager

AFURLSessionManager 创建并管理着NSURLSession这个对象,NSURLSession基于NSURLSessionConfiguration。我们来分析一下AFURLSessionManager的头文件:

  • AFURLSessionManager实现了六个协议,其中包含:NSURLSessionDelegateNSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate。
  • 属性session:关于NSURLSession,可以查看官方文档。相对于NSURLConnection ,NSURLSession强大的功能是支持后台上传和下载。不过值得注意的是,这个对象与它的delegate之间的是一个强引用关系,因此在释放NSURLSession时,要做好处理。

  • 属性operationQueue:为NSURLSession 绑定一个队列,用于请求过程中的一系列事件在哪个 OperationQueue 回调,这里是设置了最大并发量为1的队列。

  • 属性responseSerializer:序列化响应数据的对象,默认的模式是AFJSONResponseSerializer,而且不能为空。

  • 属性securityPolicy:安全策略,默认是defaultPolicy。

  • 属性reachabilityManager:网络监控管理者。
  • 当前任务管理集合的相应属性:tasks、dataTasks、uploadTasks、downloadTasks。
  • 属性completionQueue:请求成功后,回调block会在这个队列中调用,如果为空,就在主队列。
  • 属性completionGroup:请求成功后,回调block会在这个组中调用,如果为空,就使用一个私有的。
  • 属性attemptsToRecreateUploadTasksForBackgroundSessions:用来解决在后台创建上传任务返回nil的bug,默认为NO,如果设为YES,在后台创建上传任务失败会尝试重新创建该任务。

  • 其他:相应的方法,应该看一下名称能大概猜到功能。

 每当有一个新的任务,都会创建一个对应的AFURLSessionManagerTaskDelegate,并且以task的@(taskIdentifier)作为key,AFURLSessionManagerTaskDelegate作为值保存在字典里。AFURLSessionManagerTaskDelegate代理对象用于处理上传或下载的进度以及处理获取完数据后的行为。从它的一系列属性可以看到:

@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>

@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;  //!<用于接收数据
@property (nonatomic, strong) NSProgress *uploadProgress;  //!<上传进度
@property (nonatomic, strong) NSProgress *downloadProgress;  //!<下载进度
@property (nonatomic, copy) NSURL *downloadFileURL;  //!<下载路径
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;  //!<用于返回下载完成后的文件路径
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;  //!<上传时调用
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;  //!<下载时调用
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;  //!<完成时调用

@end

2.4.2AFHTTPSessionManager

AFHTTPSessionManager是AFURLSessionManager的子类,是专门为HTTP请求设计的。它提供了一系列方法来帮助我们创建具体的请求,是我们在实际开发中经常使用的。

3.GofNetComponent的实现

通过上面对AFNetworking的介绍,我们接下来一步步讲解GofNetComponent组件。首先,提出三个问题:

问题一:使用什么方式和业务层做数据对接?

问题二:交付什么样的数据给业务层?

问题三:采用集约型还是离散型的调用方式?

这几个问题,是在做GofNetComponent组件过程中,一直在思考的问题,组件开发的过程中,也是就这几个问题,调整了部分策略。现在针对这几个问题,谈谈个人的想法。

问题一:使用什么方式和业务层做数据对接?

网络层和业务层的数据对接,业界现在使用的方式大致有这几种:Delegate,Notification,Block,KVO,Target-Action。使用频率高的主要有两种:Delegate,Block。阿里的网络层Delegate、Block以及target-action都使用了,边锋主要是采用的block,大智慧主要采用的是Notification,安居客早期以Block为主,后面改成了以Delegate为主,我们目前是使用的block。这个没有对错与否的区别,只是使用习惯和架构师的风格。Block很灵活,代码都写在一起,显得紧凑。但是在一段代码里,包含我们请求时的参数组装、参数的判断、请求发起、返回数据校验、各种判断的弹出提示、对返回数据进行处理,这些会让请求这段代码变得非常臃肿。当然,业务层也可以为Block开一个单独的方法来处理返回结果,但是这样可能导致各个业务处理的方法名不一致,可读性差,因此还不如在网络层架构的时候,就直接使用代理的方式给到业务开发,这样就把请求和返回分离了,代码结构更清晰,方法命名也更统一。

综上所述,在实际业务层的开发过程中,建议以Delegate为主,Block为辅。

问题二:交付什么样的数据给业务层?

这个在最开始的时候,是在GofNetComponent组件中集成YYModel,直接在组件中进行处理,并返回Model数据。后面思考了一下,感觉这样不好。一方面是导致GofNetComponent依赖YYModel,独立性更差;另一方面是如果直接返回model的话,对业务层限制太死了,因此个人感觉网络组件这个时候应该更灵活。

问题三:采用集约型还是离散型的调用方式?

首先解释一下这两个概念:

  • 集约型API:所有API的调用只有一个类,然后这个类接收API名字、API参数、以及回调着陆点(可以是target-action、block、delegate等)作为参数。然后执行类似startRequest这样的方法,它就会去根据这些参数起飞去调用API了,然后获得API数据之后再根据指定的着陆点去着陆。例如我们的HDFNet。
  • 离散型API:一个API对应于一个APIManager类,然后API名字、API参数、着陆方式等都已经集成入APIManager中。

理解概念之后,我们从几个角度来分析一下这两种类型:

复杂度:集约型API对于编码的要求要低一些,一方面是因为没有了基类和各种继承关系,所需要的源代码文件就要少很多;另一方面离散型API每一个请求类的 URL、参数等等都需要单独处理;

灵活性:离散型API在灵活性上是要强出很多的,通过不同的配置,可以对接口实现更多个性化的控制。

模块化:集约型API实现了层次之间的解耦,但是没有实现横向的模块化。假设有不同的人在开发不同的业务,集约型的设计很可能会导致大家都要去修改同一个文件。而离散型的设计则完全避免了这一点,对于不同的业务来说,修改的文件不会是同一个,互相之间不会产生依赖,在此基础上可以很方便地对于代码进行模块化的分割。

通过上面三个问题的分析,大家对网络层的设计思想应该有个大致的了解,下面我们来看看GofNetComponent的类图:

对于上面的类图,我们简单介绍一下各个类的职责:

  • GofNetworkAgent:负责管理所有的网络请求;
  • GofBaseRequest:网络请求基类,提供网络请求类的基本操作与接口;
  • GofRequest:继承了GofBaseRequest 的所有功能,此外增加了对缓存的处理,是所有增加缓存功能请求的基类,我们项目中需要缓存的请求类都需要直接或间接继承与它;
  • GofBatchRequest:批量网络请求类,通过此类来实现批量发送请求;
  • GofNetConfig:负责统一为网络请求加上一些参数,或者修改一些路径;
  • GofNetMonitoring:网络状态监测类;
  • GofNetworkUtils:工具类,提供一些参数加密和JSON数据检查的接口;
  • GofUrlArgumentsFilter:添加公用参数;
  • GofNetResponse:网络返回数据初步解析;

4.GofNetComponent的功能

通过上面部分的介绍,那么GofNetComponent都提供了哪些功能呢?

  • 支持网络状态监测;
  • 支持网络的自动检查,无网则不发送任何请求;
  • 支持自动检测请求发送状态,避免同一请求多次提交;
  • 支持按时间、版本号、设置版本缓存网络请求内容(包括下载文件);
  • 支持统一设置/变更服务器地址;
  • 支持检查返回 JSON 内容的合法性;
  • 支持文件的断点续传;
  • 支持 block 和 delegate 两种模式的回调方式;
  • 支持批量的网络请求发送,并统一设置它们的回调
  • 支持网络请求公用参数的统一添加(可多次添加);

 

posted @ 2019-05-23 21:31  LeeGof  阅读(450)  评论(0)    收藏  举报