美文网首页网络请求
YTKNetWork源码解析

YTKNetWork源码解析

作者: HarlanHuang | 来源:发表于2018-02-26 10:35 被阅读124次

    背景:

    YTKNetWork是一个开源的第三方网络请求框架,具有比较好的网络请求缓存机制的控制。近期项目中想要采取HTTP Cache来优化网络请求,需要实现以下两点:

    • 需要支持解析 Cache-Control Header, 包括 max-age 指令 和 no-cache 指令来实现强制缓存(即不需要向服务器发请求, 直接使用本地缓存)。
    • 需要支持解析 Etag, 发送 If-None-Match, 来实现对比缓存。

    经过简单的调研发现,YTKNetWork虽然底层是使用的AFNetWorking的框架,但是使用AFNetWorking能够通过设置缓存空间和缓存协议就能快速简单实现的方式在YTKNetWork中并没有生效。YTKNetWork已经很少维护了,所以,只能自己动手来分析YTKNetWork的实现。

    废话不多说,下面就开始吧。


    一、项目结构分析

    先上两张图

    图1
    图2

    首先,我们从目录中找到YTKNetWork.h的头文件,从中可以看到作者想对我们开放的基础功能模块,根据字面意思可以分为几种,一种是Request类型的,一种是Config,还一种是Agent。很容易理解,request类型的是用来发送网络请求的,config是用来配置请求信息的,agent暂时不清楚,用到时我们再来具体分析。那么项目结构就很清晰了,我们就一步一步来分析就好了。

    二、YTKNetworkConfig

    我们从这个最简单的单体类来入手。

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @class YTKBaseRequest;
    @class AFSecurityPolicy;
    
    -- 声明了YTKUrlFilterProtocol协议,用来在发送请求之前给请求添加普通的参数
    
    @protocol YTKUrlFilterProtocol <NSObject>
    ///  Preprocess request URL before actually sending them.
    ///
    ///  @param originUrl request's origin URL, which is returned by `requestUrl`
    ///  @param request   request itself
    ///
    ///  @return A new url which will be used as a new `requestUrl`
    - (NSString *)filterUrl:(NSString *)originUrl withRequest:(YTKBaseRequest *)request;
    @end
    
    -- 声明了YTKCacheDirPathFilterProtocol协议,在缓存请求结果的时候用来追加普通的路径信息
    
    @protocol YTKCacheDirPathFilterProtocol <NSObject>
    ///  Preprocess cache path before actually saving them.
    ///
    ///  @param originPath original base cache path, which is generated in `YTKRequest` class.
    ///  @param request    request itself
    ///
    ///  @return A new path which will be used as base path when caching.
    - (NSString *)filterCacheDirPath:(NSString *)originPath withRequest:(YTKBaseRequest *)request;
    @end
    
    -- YTKNetworkConfig这个类保存了全局的网络请求配置信息,会在YTKNetworkAgent中使用,可以格式化以及过滤请求,还可以缓存响应结果
    
    @interface YTKNetworkConfig : NSObject
    
    -- 这两个方法不能使用
    - (instancetype)init NS_UNAVAILABLE;
    + (instancetype)new NS_UNAVAILABLE;
    
    -- 使用类方法创建单例对象
    + (YTKNetworkConfig *)sharedConfig;
    
    -- 请求的根URL,默认是空字符串
    @property (nonatomic, strong) NSString *baseUrl;
    
    -- 请求CDN URL,默认是空字符串
    @property (nonatomic, strong) NSString *cdnUrl;
    
    -- URL过滤池(YTKUrlFilterProtocol协议使用)
    @property (nonatomic, strong, readonly) NSArray<id<YTKUrlFilterProtocol>> *urlFilters;
    
    -- 缓存路径的过滤池(YTKCacheDirPathFilterProtocol协议使用)
    @property (nonatomic, strong, readonly) NSArray<id<YTKCacheDirPathFilterProtocol>> *cacheDirPathFilters;
    
    -- 同AFNetworking中使用的安全策略
    @property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
    
    -- 是否记录调试信息,默认是NO
    @property (nonatomic) BOOL debugLogEnabled;
    
    -- 用来初始化AFHTTPSessionManager,默认是nil
    @property (nonatomic, strong) NSURLSessionConfiguration* sessionConfiguration;
    
    -- 添加一个新的URL过滤器
    - (void)addUrlFilter:(id<YTKUrlFilterProtocol>)filter;
    
    -- 删除所有的URL过滤器
    - (void)clearUrlFilter;
    
    -- 添加一个新的缓存地址过滤器
    - (void)addCacheDirPathFilter:(id<YTKCacheDirPathFilterProtocol>)filter;
    
    -- 删除所有的缓存地址过滤器
    - (void)clearCacheDirPathFilter;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    m文件中没什么可以分析的,就是对两个过滤池数组的增删操作,唯一注意的就是AFSecurityPolicy初始化的时候使用了defaultPolicy类方法实例化

    - (instancetype)init {
        self = [super init];
        if (self) {
            _baseUrl = @"";
            _cdnUrl = @"";
            _urlFilters = [NSMutableArray array];
            _cacheDirPathFilters = [NSMutableArray array];
            _securityPolicy = [AFSecurityPolicy defaultPolicy];
            _debugLogEnabled = NO;
        }
        return self;
    }
    -- 点进去我们可以看到就是默认不使用SSL安全策略
    + (instancetype)defaultPolicy {
        AFSecurityPolicy *securityPolicy = [[self alloc] init];
        securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
    
        return securityPolicy;
    }
    
    typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
        AFSSLPinningModeNone,
        AFSSLPinningModePublicKey,
        AFSSLPinningModeCertificate,
    };
    
    

    总结下来,YTKNetworkConfig这个类的作用就是设置base URL、安全策略、url的过滤、缓存路径的过滤。

    但是现在对于两个协议的具体使用方式还有疑问,我们带着这个疑问继续往下看。

    三、YTKBaseRequest

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    FOUNDATION_EXPORT NSString *const YTKRequestValidationErrorDomain;
    
    NS_ENUM(NSInteger) {
        YTKRequestValidationErrorInvalidStatusCode = -8,
        YTKRequestValidationErrorInvalidJSONFormat = -9,
    };
    
    // HTTP 请求方式
    typedef NS_ENUM(NSInteger, YTKRequestMethod) {
        YTKRequestMethodGET = 0,
        YTKRequestMethodPOST,
        YTKRequestMethodHEAD,
        YTKRequestMethodPUT,
        YTKRequestMethodDELETE,
        YTKRequestMethodPATCH,
    };
    
    // 请求数据序列化的方式 HTTP还是JSON
    typedef NS_ENUM(NSInteger, YTKRequestSerializerType) {
        YTKRequestSerializerTypeHTTP = 0,
        YTKRequestSerializerTypeJSON,
    };
    
    // 返回数据的序列化方式,决定了responseObject的数据类型
    typedef NS_ENUM(NSInteger, YTKResponseSerializerType) {
        -- NSData
        YTKResponseSerializerTypeHTTP,
        -- JSON 对象
        YTKResponseSerializerTypeJSON,
        -- NSXMLParser
        YTKResponseSerializerTypeXMLParser,
    };
    
    // 请求的优先级
    typedef NS_ENUM(NSInteger, YTKRequestPriority) {
        YTKRequestPriorityLow = -4L,
        YTKRequestPriorityDefault = 0,
        YTKRequestPriorityHigh = 4,
    };
    
    // 声明了3个block
    @protocol AFMultipartFormData;
    
    typedef void (^AFConstructingBlock)(id<AFMultipartFormData> formData);
    typedef void (^AFURLSessionTaskProgressBlock)(NSProgress *);
    
    @class YTKBaseRequest;
    
    typedef void(^YTKRequestCompletionBlock)(__kindof YTKBaseRequest *request);
    
    // 声明了YTKRequestDelegate协议,定义了一系列可以用来接受网络相关的消息的方法,所有的代理方法将在主队列中调用
    @protocol YTKRequestDelegate <NSObject>
    
    @optional
    // 请求成功结束
    - (void)requestFinished:(__kindof YTKBaseRequest *)request;
    // 请求失败
    - (void)requestFailed:(__kindof YTKBaseRequest *)request;
    
    @end
    
    // YTKRequestAccessory协议定义了一系列用来跟踪请求状态的方法,所有的代理方法将在主队列中调用
    @protocol YTKRequestAccessory <NSObject>
    
    @optional
    
    // 请求即将开始
    - (void)requestWillStart:(id)request;
    
    // 请求即将结束(这个方法将在调用requestFinished和successCompletionBlock前执行)
    - (void)requestWillStop:(id)request;
    
    // 请求已经结束(这个方法将在调用requestFinished和successCompletionBlock后执行)
    - (void)requestDidStop:(id)request;
    
    @end
    
    // YTKBaseRequest是网络请求的抽象类,它提供了许多选项用于构建请求,是YTKRequest的基类
    @interface YTKBaseRequest : NSObject
    
    #pragma mark - Request and Response Information
    ///=============================================================================
    /// @name Request and Response Information
    ///=============================================================================
    
    // NSURLSessionTask底层相关的
    
    // 在请求开始之前这个值是空且不应该被访问
    @property (nonatomic, strong, readonly) NSURLSessionTask *requestTask;
    
    // 就是requestTask.currentRequest
    @property (nonatomic, strong, readonly) NSURLRequest *currentRequest;
    
    // 就是requestTask.originalRequest
    @property (nonatomic, strong, readonly) NSURLRequest *originalRequest;
    
    // 就是requestTask.response
    @property (nonatomic, strong, readonly) NSHTTPURLResponse *response;
    
    ///  The response status code.
    @property (nonatomic, readonly) NSInteger responseStatusCode;
    
    ///  The response header fields.
    @property (nonatomic, strong, readonly, nullable) NSDictionary *responseHeaders;
    
    // 响应的数据表现形式,请求失败则是nil
    @property (nonatomic, strong, readonly, nullable) NSData *responseData;
    
    // 响应的字符串表现形式,请求失败则是nil
    @property (nonatomic, strong, readonly, nullable) NSString *responseString;
    
    ///  This serialized response object. The actual type of this object is determined by
    ///  `YTKResponseSerializerType`. Note this value can be nil if request failed.
    ///
    ///  @discussion If `resumableDownloadPath` and DownloadTask is using, this value will
    ///              be the path to which file is successfully saved (NSURL), or nil if request failed.
    // 
    @property (nonatomic, strong, readonly, nullable) id responseObject;
    
    // 如果设置响应序列化方式是YTKResponseSerializerTypeJSON,这个就是响应结果序列化后的对象
    @property (nonatomic, strong, readonly, nullable) id responseJSONObject;
    
    // 请求序列化错误或者网络错误,默认是nil
    @property (nonatomic, strong, readonly, nullable) NSError *error;
    
    // 请求任务是否已经取消(self.requestTask.state == NSURLSessionTaskStateCanceling)
    @property (nonatomic, readonly, getter=isCancelled) BOOL cancelled;
    
    // 请求任务是否在执行(self.requestTask.state == NSURLSessionTaskStateRunning)
    @property (nonatomic, readonly, getter=isExecuting) BOOL executing;
    
    
    #pragma mark - Request Configuration
    ///=============================================================================
    /// @name Request Configuration
    ///=============================================================================
    
    // tag可以用来标识请求,默认是0
    @property (nonatomic) NSInteger tag;
    
    // userInfo可以用来存储请求的附加信息,默认是nil
    @property (nonatomic, strong, nullable) NSDictionary *userInfo;
    
    // 请求的代理,如果使用了block回调就可以忽略这个,默认为nil
    @property (nonatomic, weak, nullable) id<YTKRequestDelegate> delegate;
    
    // 请求成功的回调,如果block存在并且requestFinished代理方法也实现了的话,两个都会被调用,先调用代理,再在主队列中调用block
    @property (nonatomic, copy, nullable) YTKRequestCompletionBlock successCompletionBlock;
    
    // 请求失败的回调,如果block存在并且requestFailed代理方法也实现了的话,两个都会被调用,先调用代理,再在主队列中调用block
    @property (nonatomic, copy, nullable) YTKRequestCompletionBlock failureCompletionBlock;
    
    // 设置附加对象(这是什么鬼?)如果调用addAccessory来增加,这个数组会自动创建,默认是nil
    @property (nonatomic, strong, nullable) NSMutableArray<id<YTKRequestAccessory>> *requestAccessories;
    
    // 可以用于在POST请求中需要时构造HTTP body,默认是nil
    @property (nonatomic, copy, nullable) AFConstructingBlock constructingBodyBlock;
    
    // 设置断点续传下载请求的地址,默认是nil
    // 在请求开始之前,路径上的文件将被删除。如果请求成功,文件将会自动保存到这个路径,否则响应将被保存到responseData和responseString中。为了实现这个工作,服务器必须支持Range并且响应需要支持`Last-Modified`和`Etag`,具体了解NSURLSessionDownloadTask
    @property (nonatomic, strong, nullable) NSString *resumableDownloadPath;
    
    // 捕获下载进度,也可以看看resumableDownloadPath
    @property (nonatomic, copy, nullable) AFURLSessionTaskProgressBlock resumableDownloadProgressBlock;
    
    // 设置请求优先级,在iOS8 + 可用,默认是YTKRequestPriorityDefault = 0
    @property (nonatomic) YTKRequestPriority requestPriority;
    
    // 设置请求完成回调block
    - (void)setCompletionBlockWithSuccess:(nullable YTKRequestCompletionBlock)success
                                  failure:(nullable YTKRequestCompletionBlock)failure;
    
    // 清除请求回调block
    - (void)clearCompletionBlock;
    
    // 添加遵循YTKRequestAccessory协议的请求对象,相关的requestAccessories
    - (void)addAccessory:(id<YTKRequestAccessory>)accessory;
    
    #pragma mark - Request Action
    // 将当前self网络请求加入请求队列,并且开始请求
    - (void)start;
    
    // 从请求队列中移除self网络请求,并且取消请求
    - (void)stop;
    
    // 使用带有成功失败blcok回调的方法开始请求(储存block,调用start)
    - (void)startWithCompletionBlockWithSuccess:(nullable YTKRequestCompletionBlock)success
                                        failure:(nullable YTKRequestCompletionBlock)failure;
    
    #pragma mark - Subclass Override
    ///=============================================================================
    /// @name Subclass Override
    ///=============================================================================
    
    // 请求成功后,在切换到主线程之前,在后台线程上调用。要注意,如果加载了缓存,则将在主线程上调用此方法,就像`request Complete Filter`一样。
    - (void)requestCompletePreprocessor;
    // 请求成功时会在主线程被调用
    - (void)requestCompleteFilter;
    // 请求成功后,在切换到主线程之前,在后台线程上调用。
    - (void)requestFailedPreprocessor;
    // 请求失败时会在主线程被调用
    - (void)requestFailedFilter;
    
    // 基础URL,应该只包含地址的主要地址部分,如http://www.example.com
    - (NSString *)baseUrl;
    // 请求地址的URL,应该只包含地址的路径部分,如/v1/user。baseUrl和requestUrl使用[NSURL URLWithString:relativeToURL]进行连接。所以要正确返回。
    // 如果requestUrl本身就是一个有效的URL,将不再和baseUrl连接,baseUrl将被忽略
    - (NSString *)requestUrl;
    // 可选的CDN请求地址
    - (NSString *)cdnUrl;
    
    // 设置请求超时时间,默认60秒.
    // 如果使用了resumableDownloadPath(NSURLSessionDownloadTask),NSURLRequest的timeoutInterval将会被完全忽略,一个有效的设置超时时间的方法就是设置NSURLSessionConfiguration的timeoutIntervalForResource属性。
    - (NSTimeInterval)requestTimeoutInterval;
    
    // 设置请求的参数
    - (nullable id)requestArgument;
    
    // 重写这个方法可以在缓存时过滤请求中的某些参数
    - (id)cacheFileNameFilterForRequestArgument:(id)argument;
    
    // 设置 HTTP 请求方式
    - (YTKRequestMethod)requestMethod;
    
    // 设置请求数据序列化的方式
    - (YTKRequestSerializerType)requestSerializerType;
    
    // 设置请求数据序列化的方式. See also `responseObject`.
    - (YTKResponseSerializerType)responseSerializerType;
    
    // 用来HTTP授权的用户名和密码,应该返回@[@"Username", @"Password"]这种格式
    - (nullable NSArray<NSString *> *)requestAuthorizationHeaderFieldArray;
    
    // 附加的HTTP 请求头
    - (nullable NSDictionary<NSString *, NSString *> *)requestHeaderFieldValueDictionary;
    
    // 用来创建完全自定义的请求,返回一个NSURLRequest,忽略`requestUrl`, `requestTimeoutInterval`,`requestArgument`, `allowsCellularAccess`, `requestMethod`,`requestSerializerType`
    - (nullable NSURLRequest *)buildCustomUrlRequest;
    
    // 发送请求时是否使用CDN
    - (BOOL)useCDN;
    
    // 是否允许请求使用蜂窝网络,默认是允许
    - (BOOL)allowsCellularAccess;
    
    // 验证 responseJSONObject 是否正确的格式化了
    - (nullable id)jsonValidator;
    
    // 验证 responseStatusCode 是否是有效的,默认是code在200-300之间是有效的
    - (BOOL)statusCodeValidator;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    相关文章

      网友评论

      • 指尖猿:大佬,求请教一个YTKNework的使用问题.可以帮帮忙吗

      本文标题:YTKNetWork源码解析

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