AFNetworking 3.0 详解
整体架构
AFNetworking 3.0 实现完全基于NSURLSessionTask进行封装,NSURLSessionTask 是苹果在iOS7 推出的网络请求api。AF支持https,网络数据请求,文件上传,文件下载,监听手机网络状态。AFHttpSessionManager 继承 AFURLSessionManager 对网络请求进行管理,使用AFURLRequestSerialization 对网络请求进行封装,使用AFURLReponseSerialization 响应体进行处理,使用AFSecurityPolicy 对服务器证书进行校验。支持https协议,支持本地证书和服务器证书进行对比验证,AF要求ios7或以上系统。AF数据传递主要使用block 和 notifacation的方式。
AFURLSessionManager 使用方法
1.请求服务器数据
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
2.上传数据
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
3.多线程下载数据
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
**
====
AFHttpSessionManager 使用方法
**
1.post请求
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
2.get请求
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
3.上传
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
还封装了如head deleate等请求方式,常用的是以上几种
AFSecurityPolicy
NSURL *googleCertificateURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"google.com" withExtension:@"cer"];
NSData *googleCertificateData = [NSData dataWithContentsOfURL:googleCertificateURL];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://apple.com/"]];
[manager setResponseSerializer:[AFHTTPResponseSerializer serializer]];
manager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate withPinnedCertificates:[NSSet setWithObject:googleCertificateData]];
证书文件放在应用bundle 中 ,会把服务器证书和本地证书对比认证,加强网络通信的安全性,判断是否信任服务器证书过程
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
// https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html
// According to the docs, you should only trust your provided certs for evaluation.
// Pinned certificates are added to the trust. Without pinned certificates,
// there is nothing to evaluate against.
//
// From Apple Docs:
// "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors).
// Instead, add your own (self-signed) CA certificate to the list of trusted anchors."
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
//服务器证书和存储在应用中的证书进行对比验证
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
AFURLSessionManager
AFURLSessionManager管理所有的请求,session 设置了NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 实现证书合法性校验,数据传输进度检测,数据请求成功或失败的回调。
使用runtime 用af_supend 替换 suspend,用af_resume 替换了resume 当调用这两个方法的时候往上层发送通知AFNetworkingTaskDidSuspendNotification AFNetworkingTaskDidResumeNotification
Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
}
if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
}