网络请求

YTKNetWork源码解析

2018-02-26  本文已影响124人  HarlanHuang

背景:

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

经过简单的调研发现,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
上一篇下一篇

猜你喜欢

热点阅读