从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
1891

被折叠的 条评论
为什么被折叠?



