NSURLSession请求网络数据

在平时开发中一直使用的是AFNetworking,时间久了感觉苹果原生的NSURLSession都不会用了,这篇文章简单记录下NSURLSession进行网络数据请求,方便以后查阅复习之用。

一、构造NSMutableURLRequest

Request可以设置网络请求的URI、请求头、请求正文(POST请求时)。GET请求时需将参数直接拼接在URI后面,POST请求时需要将参数作为NSData数据放在请求Body中。request可以灵活的设置请求方法HTTPMethod ,请求URL,请求发送数据格式Content-Type等消息。

// 可以在此处拼接公共url
    NSString *strUrl = [NSString stringWithFormat:@"%@",urlStr];
    // 将URL中的空白全部去掉,防止url前后有空白
    NSString *urlPath = [strUrl stringByReplacingOccurrencesOfString:@" " withString:@""];
    NSURL *url = [NSURL URLWithString:urlPath];
    
    // 构造request
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
    request.timeoutInterval = 30;
    NSString *methodUpperCase = [method uppercaseString];
    // 参数拼接
    NSMutableString *paramsString = [[NSMutableString alloc] init];
   //  取出参数中所有的key值
    NSArray *allKeysFromDic = params.allKeys;
    for (int i =0 ; i < params.count; i++)
    {
        NSString *key = allKeysFromDic[i];
        NSString *value = params[key];
        
        [paramsString appendFormat:@"%@=%@",key,value];
        // 每个关键字之间都用&隔开
        if (i < params.count - 1)
        {
            [paramsString appendString:@"&"];
        }
    }
    
    if ([methodUpperCase isEqualToString:@"GET"]) // get请求就只有URL
    {
        // GET请求需要拼接
        NSString *separe = url.query ? @"&" : @"";
        NSString *paramsURL = [NSString stringWithFormat:@"%@%@%@",urlPath,separe,paramsString];
        request.URL = [NSURL URLWithString:paramsURL];
        request.HTTPMethod = @"GET";
    }
    else if ([methodUpperCase isEqualToString:@"POST"]) // POST请求就需要设置请求正文
    {
        NSData *bodyData = [paramsString dataUsingEncoding:NSUTF8StringEncoding];
        request.URL = url;
        request.HTTPBody = bodyData;
        request.HTTPMethod = @"POST";
    }
    else // 暂时没有考虑DELETE PUT等其他方式
    {
        return;
    }
    // 设置请求数据的content-type
    //  [reuqest setValue:contentType forHTTPHeaderField:@"Content-Type"];

经常有后台要求移动端设置请求头数据格式,就是在此步完成的啦。

二、NSURLSessionConfiguration设置

NSURLSessionConfiguration可以设置请求超时时间、请求头信息(如设置希望服务器返回数据格式信息等)、缓存策略、是否允许使用流量等信息。

// NSURLSessionConfiguration 设置请求超时时间, 请求头等信息
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    // 设置请求超时为30秒钟
    configuration.timeoutIntervalForRequest = 30;
    // 设置在流量情况下是否继续请求
    configuration.allowsCellularAccess = YES;
    // 设置请求的header
    configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json,text/html,text/plain",
                                            @"Accept-Language": @"en"};

三、创建NSURLSession

好像现在苹果比较新的API中,都会引入Session这个词, 有点管理、关联的意思在里面。
创建NSURLSession,设置代理,并发送网络请求。在代理方法中可以设置HTTPS时证书相关策略,方便监听下载数据时的进度条。

// 创建NSURLSession
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    // 发送Request
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       
        if (error)
        {
            fail(error,@"没有网络");
            return;
        }
        NSError *err  = nil;
        
        NSHTTPURLResponse *http = (NSHTTPURLResponse*)response;
        NSLog(@"%@",http.MIMEType);
        /*
         1、 服务端需要返回一段普通文本给客户端,Content-Type="text/plain"
         2 、服务端需要返回一段HTML代码给客户端 ,Content-Type="text/html"
         3 、服务端需要返回一段XML代码给客户端 ,Content-Type="text/xml"
         4 、服务端需要返回一段javascript代码给客户端,text/javascript
         5 、服务端需要返回一段json串给客户端,application/Json
         */
        if ([http.MIMEType isEqualToString:@"text/html"])
        {
            NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            succ(html);
        }
        else
        {
            id res =  [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves | NSJSONReadingAllowFragments error:&err];
            if (err)
            {
                fail(err, @"数据解析错误");
                return;
            }
            succ(res);
        }
    }];
    [task resume]; // 开始网络请求

在NSURLSessionDelegate中可以设置HTTPS请求是的证书使用策略情况

// 只有HTTPS请求才会回调这个函数
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    NSLog(@"didReceiveChallenge ");
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) // 服务器信任证书
    {
        NSLog(@"server ---------");
        NSString *host = challenge.protectionSpace.host;
        NSLog(@"%@", host);
        NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    }
    else if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) // 输入密码的证书,校验自己的证书
    {
        //客户端证书认证
        //TODO:设置客户端证书认证
        // load cert
        NSLog(@"client");
        NSString *path = [[NSBundle mainBundle]pathForResource:@"client"ofType:@"p12"];
        NSData *p12data = [NSData dataWithContentsOfFile:path];
        CFDataRef inP12data = (__bridge CFDataRef)p12data;
        SecIdentityRef myIdentity;
        OSStatus status = [self extractIdentity:inP12data toIdentity:&myIdentity];
        if (status != 0)
        {
            return;
        }
        SecCertificateRef myCertificate;
        SecIdentityCopyCertificate(myIdentity, &myCertificate);
        const void *certs[] = { myCertificate };
        CFArrayRef certsArray =CFArrayCreate(NULL, certs,1,NULL);
        NSURLCredential *credential = [NSURLCredential credentialWithIdentity:myIdentity certificates:(__bridge NSArray*)certsArray persistence:NSURLCredentialPersistencePermanent];
        //         网上很多错误代码如上,正确的为:
        completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
    }
}

- (OSStatus)extractIdentity:(CFDataRef)inP12Data toIdentity:(SecIdentityRef*)identity {
    OSStatus securityError = errSecSuccess;
    CFStringRef password = CFSTR("123456");
    const void *keys[] = { kSecImportExportPassphrase };
    const void *values[] = { password };
    CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
    CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
    securityError = SecPKCS12Import(inP12Data, options, &items);
    if (securityError == 0)
    {
        CFDictionaryRef ident = CFArrayGetValueAtIndex(items,0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue(ident, kSecImportItemIdentity);
        *identity = (SecIdentityRef)tempIdentity;
    }
    else
    {
        NSLog(@"clinet.p12 error!");
    }
    
    if (options)
    {
        CFRelease(options);
    }
    return securityError;
}

总结:NSURLRequest更像一个模型,只负责传递URL、请求方法、请求参数、请求Content-Type等数据;NSURLSessionConfiguration则偏重于设置缓存机制,已经网络使用情况的配置;NSURLSession主要负责数据的处理过程,方便在其代理方法中进行各种操作;NSURLSessionDataTask则管理网络请求的生命周期。

将Demo上传到Github

AFNetworking基本也是按上面的方法实现的,只是代码写的更加规范、更加健壮和通用。自己写一次NSURLSession之后,再看一次AFNetworking的源码,感觉对其理解更进一步,佩服Matt大神啊!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 在iOS开发中,我们常用第三方—AFNetWorking框架请求网络数据,现在已经更新到了3.0,对NSURLSe...
    行走的苹果哥阅读 1,189评论 0 1
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,629评论 2 7
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 在苹果彻底弃用NSURLConnection之后自己总结的一个网上的内容,加上自己写的小Demo,很多都是借鉴网络...
    付寒宇阅读 4,249评论 2 13
  • 目录 网络基本概念 TCP/IP协议簇基本概念 HTTP 网络开发技术解决方案 数据解析 网络优化 1. 网络基本...
    Ryan___阅读 1,346评论 1 0