- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 设置初始化NSURLSessionConfiguration
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 设置操作队列及控制最大并发数
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
// 设置NSURLSession
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// 设置默认最后解析接口返回数据类(用什么方式去解析)
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 设置一些服务器认证请求
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// 实时监控当前网络状况
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
// 加锁
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
// 这是为了防止后台回来,重新初始化这个session,一些之前的后台请求任务,导致程序的crash。
// 将所有回调清nil
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// 拼接路径
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
// 设置baseURL
self.baseURL = url;
// 请求序列化类
self.requestSerializer = [AFHTTPRequestSerializer serializer];
// 响应序列化类
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
AFHTTPSessionManager继承自AFURLSessionManager类,我们可以通过[AFHTTPSessionManager manager]生成单例对象,会调用initWithBaseURL初始化生成,在生成的初始化对象中设置了响应及请求序列类、会话类NSURLSession及服务器请求认证类!
// GET
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
headers:(nullable NSDictionary *)headers
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
// HEAD
- (NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary *)headers
success:(void (^)(NSURLSessionDataTask * _Nonnull))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
// POST
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
// PUT
- (NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary *)headers
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
// PATCH
- (NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary *)headers
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
// DELETE
- (NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary *)headers
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
这是AFN中可用于进行网络请求方法,我们可根据需要选择相应的请求方法:
// 开启GET方法
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
headers:(nullable NSDictionary *)headers
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
// 设置拼接请求参数body和首部字段
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
for (NSString *headerField in headers.
keyEnumerator) {
// 添加客户端设置的请求首部字段参数
[request addValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// 通过request开启任务
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
通过设置请求对象Request来设置请求首部字段及请求参数,并通过dataTaskWithHTTPMethod生成任务对象task并开启任务,让我们继续来一步步研究!
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
// 设置请求方式
mutableRequest.HTTPMethod = method;
// 通过监听用户mutableRequest里的属性值是否改变,若有则将该属性及属性值设置给mutableRequest
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 设置请求参数字段
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
// 设置请求首部及请求参数字段数据body
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSMutableURLRequest *mutableRequest = [request mutableCopy];
// 用户通过当前类添加需要设置的首部属性值,在该类中是将属性值加到self.HTTPRequestHeaders中的
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) { // 若首部中未包含此属性则添加该属性到该请求的首部HTTPHeader中
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) { // 用户自定义 实现拼接参数方法
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
} else {
switch (self.queryStringSerializationStyle) { // 默认样式
case AFHTTPRequestQueryStringDefaultStyle:
// 默认样式进行将字典转换后的参数拼接字符串
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
// self.HTTPMethodsEncodingParametersInURI包含@"GET", @"HEAD", @"DELETE",使用拼接在url后面
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
//URL.query判断是否已经有数据参数,若有则直接用&拼接,否则用?拼接
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
// POST请求设置body的默认编码方式
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
// 用NSUTF8StringEncoding编码格式将query参数字符串转为NSData
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
通过对mutableRequest的相关属性设置参数值后,将返回的mutableRequest生成任务Task并开启任务,此时一个请求任务启动,等待网络请求结果!
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
// 将delegate与manage之间进行关联
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// 数据请求完成时回调AFURLSessionManagerTaskDelegate的代理方法
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
// AFURLSessionManagerTaskDelegate的代理方法
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
__strong AFURLSessionManager *manager = self.manager;
// 省略部分代码
if (error) { // 出错时的处理
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
});
} else { // 未出错时的处理
dispatch_async(url_session_manager_processing_queue(), ^{
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
}
}
完成数据请求时,会回调didCompleteWithError对数据进行相关处理,这里若manager设置了跟AFURLSessionManagerTaskDelegate关联起来,则在任务完成回调后,将调用AFURLSessionManagerTaskDelegate类中的方法对数据进行处理,数据处理的方式按照responseSerializer对应设置的类型方式进行!
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 省略部分代码
for (id serializer in self.responseSerializers) {
NSError *serializerError = nil;
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
if (responseObject) {
return responseObject;
}
}
return [super responseObjectForResponse:response data:data error:error];
}
继承自AFURLResponseSerialization生成了AFJSONResponseSerializer、AFXMLParserResponseSerializer、AFXMLDocumentResponseSerializer、AFPropertyListResponseSerializer、AFImageResponseSerializer和AFCompoundResponseSerializer类,通过生成不同的serializer类对象,有不同的数据处理方式!
随着互联网的快速发展,导致人们的隐私也变得越来越重要了!苹果爸爸一直都很注重用户隐私问题,所以也提倡应更缜密地传输用户数据信息,防止数据泄露!所以在这里也引申出了HTTPS这个概念,HTTPS具体原理可以参考我写的文章 HTTPS协议:叫个外卖咋这么复杂呢!!
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
// 自定义处理证书方式
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
// 判断服务器返回的证书是否是服务器信任的
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { // 通过认证
// 若验证通过,生成用NSURLCredential类生成证书
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
// NSURLSessionAuthChallengeUseCredential为使用正式
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
// 无证书时,忽略证书,这是系统默认的做法
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else { // 证书未通过认证,直接取消认证,忽略证书
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// 忽略证书,系统默认处理方式
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
// 使用completionHandler回调,让系统去对接完成服务器端的认证过程
completionHandler(disposition, credential);
}
}
实现HTTPS认证过程中,我们可以使用证书或非证书认证的方式,这个具体看公司需求,下面我们来看看这个具体过程!
CA机构认证证书:这种方式可以让客户端开发感觉很爽,只需要将请求URL中的http换成https后,便可以放心地进行数据传输,因为服务器端只需要将用于认证的加密证书配置为CA机构信任认证的证书即可,这样服务器在第一次加密认证时,用认证证书加密服务器公钥发送给客户端时,当加密数据到达客户端后,客户端自带的CA机构证书会认证服务器加密时使用的加密证书,若验证成功后,客户端便可取出服务器用于客户端传输数据到服务器所使用的公钥!
非CA机构认证证书:这个过程一般是使用服务器端配置的证书,而这个证书并非CA机构认证的,但我们可以先将证书给客户端,让客户端去认可这个配置的证书,完成后面的HTTPS验证过程,若验证通过则双方可进行交换数据!
AFSSLPinningModeNone:验证是否允许非权威证书(自签名)或是认证机构信任的证书,是的话则验证通过;
AFSSLPinningModeCertificate:获取客户端证书链中是否有证书包含服务器端证书链中的证书,是的话则验证通过;
AFSSLPinningModePublicKey:从客户端证书链中将证书转换为公钥key,服务器端证书链也一样转换成公钥key,通过各自的公钥key进行判断,若相等则通过验证;
3. 若证书验证通过,则生成NSURLCredential证书,使用completionHandler回调,让系统去对接完成服务器端的认证过程;
AFNetworkReachabilityManager是AFN提供的可以对网络状态进行检测,并在主线程完成监控状态的回调!苹果对需要联网的应用要求很高,就是必须要进行联网检查。另外,当网络发生异常时能够及时提示用户网络已断开,而不是程序问题造成卡顿;
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// 一共有四种状态
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"AFNetworkReachability Not Reachable");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"AFNetworkReachability Reachable is WWAN");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"AFNetworkReachability Reachable is WiFi");
break;
case AFNetworkReachabilityStatusUnknown:
default:
NSLog(@"AFNetworkReachability Unknown");
break;
}
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
当我们使用单例sharedManager简单地使用startMonitoring开启网络检测,可以实时检测到网络状态,但是存在一点不足之处:
但是这里并不能检测到服务器是否真的可达,只能检测设备是否连接到局域网,以及用的WiFi还是WWAN。即使把设备网络关了,立马检测出NotReachable,连接到路由器立马检测出的还是可以连接到WIFI !有时候虽然联网了,但是网络不一定就能上网是一种可能性,另一种可能性是数据包在传输过程中可以会受影响而被丢弃,毕竟数据包在网际层传输,每经过一个路由器默认经过一跳,但达到255跳时,路由器就会自动丢包!
/** 将字符串中包含的特殊字符进行转码 **/
NSString * AFPercentEscapedStringFromString(NSString *string) {
// 允许特殊字符转码
static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
// URL转码合集
NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
// 在合集中去掉上面的特殊字符(即使没有也执行此移除操作),相当于允许上面的特殊字符可转码
[allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
// FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028
// return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
static NSUInteger const batchSize = 50;
NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;
while (index < string.length) {
NSUInteger length = MIN(string.length - index, batchSize);
NSRange range = NSMakeRange(index, length);
// To avoid breaking up character sequences such as ????
// 将表情或字符等完整截取
range = [string rangeOfComposedCharacterSequencesForRange:range];
NSString *substring = [string substringWithRange:range];
// 将特殊字符进行转码
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
[escaped appendString:encoded];
index += range.length;
}
return escaped;
}
实现手动监听KVO