NSURLSession-HTTP同步和异步请求

从iOS9苹果废除NSURLConnection,建议用NSURLSession

NSURLSession没有直接同步请求的方法。想使用NSURLSession进行同步请求,即数据获取后才继续执行后面代码,使用信号、信号量可以实现。

这里先贴上异步请求代码



typedef enum {
    HttpRequestTypePost,
    HttpRequestTypeGet
}HttpRequestType;

typedef void(^HttpSuccessBlock)(NSDictionary *successDic);
typedef void(^HttpFailedBlock)(NSError *error);


- (void)sendHttpRequestWithType:(HttpRequstType)type URL:(NSString *)url parameters:(NSDictionary *)params successBlock:(HttpSuccessBlock) successBlock faildBlock:(HttpFailedBlock) failedBlock {
    
    NSURL *URL = [NSURL URLWithString:url];
    NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] initWithURL:URL];
    
    if (params != nil) {
        NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
        [theRequest setHTTPBody:postData];
    }
    
    NSString *methodType = (type == HttpRequstTypeGet ? @"GET" : @"POST");
    [theRequest setHTTPMethod:methodType];
    
    [theRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
    NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString*)@"CFBundleShortVersionString"];
    NSString *iOSVersion = [[UIDevice currentDevice] systemVersion];
    [theRequest setValue:[NSString stringWithFormat:@"%@/%@ ios/%@",@"sky",appVersion,iOSVersion] forHTTPHeaderField:@"User-Agent"];
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:theRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        if (error) {
            
            if (failedBlock) {
                failedBlock(error);
            }
            
        } else {
            if (data) {
                NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
                
                if (successBlock) {
                    successBlock(dic);
                }
            }
            
        }
    }];
    
    [dataTask resume];
    
}


其中有一步设置了session 的代理,可通过代理方法验证https请求证书


#pragma mark - https证书验证
//http请求证书不可信任,就忽略
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    
    
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        /* 调用自定义的验证过程 */
        if ([self wulianCustomValidation:challenge]) {
//            NSLog(@"证书可信任");
            completionHandler(NSURLSessionAuthChallengeUseCredential , [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust]);
        }else {
            //验证证书不通过
            //当执行到这的时候应该去排查一下具体为什么没过,可以看一下是否换证书了,是否是用GoDaddyRootCA或其直接签发的子证书签发的
//            NSLog(@"证书不可信任");
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge , nil);
        }
        
    }else {
        
//        NSLog(@"默认处理证书");
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling , nil);
        
    }
    
}

- (BOOL)wulianCustomValidation:(NSURLAuthenticationChallenge *)challenge {
    //获得工程中的证书
    // 获取cer格式CA证书路径
    NSString *cerPath;
    
    if (isTest) {
        cerPath = [[NSBundle mainBundle] pathForResource:@"WL_test.sh.gg.root" ofType:@"cer"];
        
    }else {
        cerPath = [[NSBundle mainBundle] pathForResource:@"WL_hw.pu.sh.gg.root" ofType:@"cer"];
    }
    
    // 提取二进制内容
    NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
    // 根据二进制内容提取证书信息
    SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData);
    // 形成钥匙链
    NSArray * chain = [NSArray arrayWithObject:(__bridge id)(caRef)];
    CFArrayRef caChainArrayRef = CFBridgingRetain(chain);
    // 取出服务器证书
    SecTrustRef trust = [[challenge protectionSpace] serverTrust];
    
    //初始化验证结果值
    SecTrustResultType trustResult = kSecTrustResultInvalid;
    
    int err = SecTrustSetAnchorCertificates(trust, caChainArrayRef);
    if (err == noErr) {
        // 用CA证书验证服务器证书
        err = SecTrustEvaluate(trust, &trustResult);
    }
    
    // 检查结果 kSecTrustResultConfirm 当值是这个时应询问用户许可,但这个值在iOS7.0后废弃了,系统支持到7.0 可以不管理这个值
    BOOL trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
#warning 使用根证书验证存在服务器证书如果不是我们使用的GoDaddyRootCA签发的这个子证书签发的(另一个子证书签发)也能校验过
    
    return trusted;
}

如果不需要证书验证,可以简单的进行默认设置


- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        
        NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential , card);
        
    }else {
        
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling , nil);
        
    }
    
}


在上面异步请求的基础上,封装同步请求即可,使用信号量dispatch_semaphore_t、dispatch_semaphore_signal、dispatch_semaphore_wait等待数据返回


//https同步请求
- (NSDictionary *)sendSynchronousRequestWithType:(HttpRequstType)requestType URL:(NSString *)url parameters:(NSDictionary *)params {
    
    NSDictionary __block *resultDic;
    
    dispatch_semaphore_t disp = dispatch_semaphore_create(0);
    [self sendHttpRequestWithType:requestType URL:url parameters:params successBlock:^(NSDictionary *successDic) {
        
        NSLog(@"正在数据加载2");
        resultDic = successDic;
        dispatch_semaphore_signal(disp);
        
    } faildBlock:^(NSError *error) {
        
        NSLog(@"正在数据加载3");
        resultDic = @{@"error": error};
        dispatch_semaphore_signal(disp);
        
    }];
    
    NSLog(@"正在数据加载1");
    dispatch_semaphore_wait(disp, DISPATCH_TIME_FOREVER);
    NSLog(@"数据加载完毕");
    
    return resultDic;
}

网络正常,打印的过程应该是

正在数据加载1  ->  正在数据加载2  ->  数据加载完毕

注意: 调用这个同步http请求不可以在主线程中,否则会造成卡死。需要单独开启一个线程进行数据请求

可以像下面这样

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
//进行数据请求

});


参考:

GCD中信号量的介绍  http://blog.youkuaiyun.com/eduora_meimei/article/details/23129977 

https证书验证  http://www.jianshu.com/p/31bcddf44b8d

iOS-https  http://www.jianshu.com/p/4b5d2d47833d


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值