制作HTTP和HTTPS请求 <- 网络概述

OS X和iOS提供很多用于通用目的的API来制作HTTP和HTTPS请求。使用这些API,你可以下载文件到磁盘、制作简单的HTTP和HTTPS请求、或者依据服务器基础结构的特定要求精确的调整你的请求。

当选择API时,你应该首先考虑你为什么要制作一个HTTP请求:

  • 如果你在编写一个Newsstand应用,你应该使用NKAssetDownload API来在后台下载内容。
  • 如果你需要下载文件到OS X的磁盘,使用NSURLDownload类是最容易的方式。详情参见 Downloading the Contents of a URL to Disk。
  • 如果是下面这几个原因,你应该使用CFHTTPStream:
  • 你有不使用OC的明确需求。
  • 你需要重写代理设置。
  • 你需要与特定的不兼容的服务器兼容。
  • 更多信息,参见Making Requests Using Core Foundation。
  • 否则,通常你应该使用NSURLSession 或 NSURLConnection API。

下面的部分描述了这些API的更多细节。

注意:如果你有特定的需要,你也可以使用socket或socket流API编写自己的HTTP委托实现。这些API的描述在Using Sockets and Socket Streams中。

使用Foundation制作需求

下面的任务描述了使用NSURLSession 类、NSURLConnection类、以及相关类的常见操作。

不使用委托检索URL的内容

如果你只是要检索URL中的内容,并对得到的结果进行操作,那么在OS Xv10.9和iOS 7及更高的版本中,你应该使用NSURLSession类。你还可以使用NSURLConnection类来完成OS X和iOS较早版本的相同任务。

为此,请调用以下方法之一:dataTaskWithRequest:completionHandler: (NSURLSession), dataTaskWithURL:completionHandler: (NSURLSession), 或 sendAsynchronousRequest:queue:completionHandler: (NSURLConnection). 你的应用必须提供下列信息:

  • 根据需要,提供NSURL对象,或者是提供了URL、body data、以及被特定需求信息填充的NSURLRequest对象。
  • 一个完成处理器代码块(completion handler block),它在传输结束或者失败的时候运行。
  • 对于NSURLConnection,在其代码块上的NSOperation队列将运行。

如果传输成功,请求的内容会以NSData对象和NSURLResponse对象的形式被传递给回调处理器代码块。如果URL加载系统无法检索到URL的内容,一个NSError对象将被传递给第三个参数。

使用委托检索URL的内容

如果应用需要对请求进行更多的控制(例如控制是否遵循重定向、执行自定义的身份验证、或者以接收到的方式接收分段数据)你可以使用带有自定义委托的NSURLSession类。为了和之前版本OS X和iOS兼容,你还可以使用带有委托的NSURLConnection类。

在大多数情况下,NSURLSession 和 NSURLConnection 类在高级层面的工作方式很类似。但是,它们也有一些显著的区别:

  • NSURLSession API提供了和NSURLDownload类行为类似的下载任务的支持。这一用法的进一步描述在Downloading the Contents of a URL to Disk中。
  • 当你创建NSURLSession对象是,你提供一个可重用的配置对象,它封装了很多常用的配置选项。使用NSURLConnection,你必须在每次连接时单独的设置这些选项。
  • NSURLConnection对象只能处理单一的请求及其后续的请求。NSURLSession对象可以管理多个任务,每个任务代表一个URL请求及其后续的请求。你通常在应用启动的时候创建会话,然后和创建NSURLConnection对象一样的方式创建任务。
  • 对于NSURLConnection,每个连接对象都有一个独立的委托。而NSURLSession,委托在会话中是所有任务共享的。如果你需要使用不同委托。你必须创建新的会话。

买默认的运行循环模式下,当你初始化 NSURLSession 或 NSURLConnection对象时,连接和会话会被自动安排在当前的运行循环内。

你提供的委托,会在连接处理过程中接收到通知,包括当连接接收到来自于服务器的额外数据时,间歇的调用URLSession:dataTask:didReceiveData: 或 connection:didReceiveData:方法。应用的委托的责任是,在必要时对接收到的数据进行跟踪。规则是:

  • 如果一次可以处理一段数据,那么就照此执行。例如,你可以使用流XML解析器。
  • 如果数据很小,你可以把它添加到NSMutableData对象。
  • 如果数据很大,你应该把它写入一个文件,然后在传输完成之后进行处理。

当URLSession:task:didCompleteWithError: 或 connectionDidFinishLoading:方法被调用时,委托已经接收了完整的URL内容。

把URL内容下载到磁盘

在OS X v10.9及更改版本或iOS7 及更高版本,如果你需要下载URL内容并把它存储为一个文件,不需要在过程中处理数据。NSURLSession类让你一步就把URL内容以文件的形式下载到磁盘(而不是下载到内存,然后在自己进行写入)。NSURLSession类还允许你暂停及重启下载,重启失败的下载,以及因应用被挂起、崩溃、或其他原因不再运行的下载继续进行。

在iOS中,NSURLSession类还可以在完成下载后从后台启动应用,以便你对该文件执行特定应用处理。

注意:在旧版本的OS X中,你还可以使用NSURLDownload 类来下载文件到磁盘。NSURLDownload类不支持在应用不运行的时候下载文件的的功能。
在旧版本的iOS中,你必须使用NSURLConnection对象把数据下载到内存,然后自己把文件写入磁盘。

想要使用NSURLSession类来下载,你的代码必须做到以下几条:

  1. 使用自定义的委托和你选择的配置对象来创建会话:
  • 如果想在应用不运行的时候继续下载,你必须在的创建会话的时候提供后台会话配置对象(使用唯一的标识符)。
  • 如果你不在意后台下载,你可以使用任何提供的会话配置对象类型来创建会话。
  1. 在会话中创建以及恢复一个或多个下载任务。
  2. 等待,直到你的委托接收到从任务或会话来的调用。具体来说,就是在文件下载完成的时候,必须实现URLSession:downloadTask:didFinishDownloadingToURL:方法来处理文件,并实现URLSession:task:didCompleteWithError:方法来处理错误。

注意:上述步骤是一个非常简化的概述;根据你的需要,你或许希望你的会话实现一些其他委托方法,以处理自定义身份验证、重定向处理等等。

制作POST请求

你可以和制作其他URL请求很相近的方式(在Retrieving the Contents of a URL with Delegates中描述)来制作HTTP或HTTPS POST请求。主要的不同点是,你必须首先配置NSMutableURLRequest对象,该对象是你要提供给initWithRequest:delegate:方法的。

你还需要构建一个body数据。你可以用下面三种方式之一来进行:

  • 对于上传内存中的短数据,你应该对现有数据进行URL编码。这个过程在Encoding URL Data中描述。
  • 对于上传来自磁盘的文件数据,调用setHTTPBodyStream:方法来告诉NSMutableURLRequest 从 NSInputStream中读取,并使用结果数据作为body内容。
  • 对于一大块结构化的数据,调用CFStreamCreateBoundPair来创建一对数据流,然后调用setHTTPBodyStream:方法来告诉NSMutableURLRequest使用其中一个数据流作为它的body内容的源。通过写入其他数据流,你可以一次发送一段数据。基于你在服务器端处理的方式,你还需要对你发送的数据进行URL编码。

想要为请求指定不同的内容类型,使用setValue:forHTTPHeaderField:方法。如果你这样做,请确保你的body数据的内容类型格式正确。

想要获取POST请求的进度估算,请实现连接委托的connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:方法。

配置认证

使用NSURLSession 和 NSURLConnection执行认证是相对简单的。你使用的方式基于你的OS X和iOS版本支持的方法。

对于NSURLSession类,你的委托应该实现URLSession:task:didReceiveChallenge:completionHandler:方法。在这个方法中,你可以执行所需的任何操作,来确定如何应对挑战,然后调用提供的完成处理器;在调用时,带有一个表明URL加载系统应该如何处理的常量,以及可选的,一个用于认证目的的凭证。

对于NSURLConnection类:

  • 在OS X v10.7及更新版本或iOS 5及更细版本,你的委托应该实现connection:willSendRequestForAuthenticationChallenge:方法。这个方法必须调用一个发送方(NSURLConnection对象)的方法,来告诉它如何继续。
  • 较早的版本中,代理应该实现connection:canAuthenticateAgainstProtectionSpace: 和 connection:didReceiveAuthenticationChallenge:两个方法。在以前的版本中connection:didReceiveAuthenticationChallenge: 方法相当于connection:willSendRequestForAuthenticationChallenge: 方法,并且调用一个发送方(NSURLConnection对象)的方法来告诉它如何继续。
  • 如果[protectionSpace authenticationMethod]是NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodHTMLForm, NSURLAuthenticationMethodNegotiate, 或NSURLAuthenticationMethodNTLM中的任何一种,connection:canAuthenticateAgainstProtectionSpace:方法应该返回YES。The connection:canAuthenticateAgainstProtectionSpace: method should return YES if [protectionSpace authenticationMethod] is any of NSURLAuthenticationMethodDefault, NSURLAuthenticationMethodHTTPBasic, NSURLAuthenticationMethodHTTPDigest, NSURLAuthenticationMethodHTMLForm, NSURLAuthenticationMethodNegotiate, or NSURLAuthenticationMethodNTLM.
认证挑战的可能响应

无论你使用哪个类,你的认证处理器方法必须检查认证挑战,并告诉URL 加载系统(URL Loading System)如何继续:

  • 想要给认证提供一个凭证,将NSURLSessionAuthChallengeUseCredential作为配置传递(对于NSURLSession),或者调用useCredential:forAuthenticationChallenge:方法(对于NSURLConnection)。关于创建拼争对象的信息,参见Creating a Credential Object。
  • 想要在没有提供认证的时候继续请求,将NSURLSessionAuthChallengeUseCredential作为有nil凭证的配置传递(对于NSURLSession),或者调用continueWithoutCredentialForAuthenticationChallenge:方法(对于NSURLConnection)。
  • 想要取消认证挑战,将NSURLSessionAuthChallengeCancelAuthenticationChallenge作为配置传递(对于NSURLSession),或者调用cancelAuthenticationChallenge:方法(对于NSURLConnection)。如果你取消认证挑战,数据流委托的错误方法将被调用
  • 想要告诉操作系统正常的处理挑战,将NSURLSessionAuthChallengePerformDefaultHandling作为配置传递(对于NSURLSession),或者调用performDefaultHandlingForAuthenticationChallenge:方法(对于NSURLConnection)。如果你请求默认处理,则操作系统会发送任何凭证缓存中已存在的合适的凭证。注意:performDefaultHandlingForAuthenticationChallenge:方法在 OS X v10.7 或 iOS 5之前不支持。
  • 想要在交涉的过程中拒绝一个特定的认证类型,打算接收一个不同的方法,将NSURLSessionAuthChallengeRejectProtectionSpace作为配置传递(对于 NSURLSession),或者调用rejectProtectionSpaceAndContinueWithChallenge:方法(对于NSURLSession)。注意:rejectProtectionSpaceAndContinueWithChallenge:方法不支持OS X v10.7 或 iOS 5之前的版本。
创建配置对象

在你的委托的connection:willSendRequestForAuthenticationChallenge: 或 connection:didReceiveAuthenticationChallenge:方法中,你必须提供NSURLCredential对象,它提供真实的认证信息。

  • 对于简单的登录/密码认证,调用credentialWithUser:password:persistence:.认证。
  • 对于基于证书的认证,调用带有SecIdentityRef对象(它通常通过调用SecItemCopyMatching,从用户钥匙串中获取)的credentialWithIdentity:certificates:persistence:方法。
进一步信息

想要了解更多NSURLSession API,请阅读URL Session Programming Guide。相关的样本代码,参见SimpleURLConnections, AdvancedURLConnections, 以及 SeismicXML: Using NSXMLParser to parse XML documents.

关于NSURLConnection API的详情,请阅读URL Session Programming Guide。

想要了解更多关于使用NSStream API来制作HTTP请求,请阅读Stream Programming Guide中的Setting Up Socket Streams。

对于setHTTPBodyStream: 方法和 CFStreamCreateBoundPair函数的例子,参见iOS 库的SimpleURLConnections。(这个例子是为iOS缩写的,但是器中网络部分的代码也可以用于OS X。)

使用Core Foundation制作请求

除了语法细节,Core Foundation中的请求功能和Foundation层的功能紧密相关。因此,例子Making Requests Using Foundation将帮助理解如何使用CFHTTPStream API来制作请求。

Core Foundation URL Access Utilities是C语言的API,它是Core Foundation 框架的一部分。想要了解更多,请阅读Core Foundation URL Access Utilities Reference。

CFHTTPStream API是Core Foundation框架的一部分。(当然你可以使用OC代码。)想要了解更多,请阅读CFNetwork Programming Guide中的Communicating with HTTP Servers 和 Communicating with Authenticating HTTP Servers。

这些API以非常灵活的方式与HTTP服务器通信(不需要直接使用socket或socket流),提供对发送到远程服务器的消息体的完全控制,并控制大多数的消息头。这些API也更复杂,因此应该高级API不能支持你的需求的时候才使用——例如,如果你需要重写默认的系统代理。

使用Web服务器

如果你想将客户端web服务通信和并到OS X程序,你可以充分利用多种技术:

  • NSJSONSerialization类实现原生的Cocoa对象和JavaScript对象表示法(JavaScript Object Notation, JSON)之间转换。
  • NSXMLParser类为XML内容的SAX风格(流)解析提供Cocoa API。
  • libxml2库为XML内容的Sax风格(流)和DOM风格(基于树)的解析提供跨平台的C API。libxml2文档,参见http://xmlsoft.org/
  • NSXMLDocument API(只针对OS X)为XML内容提供DOM风格支持。

此外,还有一些第三方库可以用于web服务。

重要:Web Services Core框架已弃用,不要用于新开发项目。

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

推荐阅读更多精彩内容

  • iOS网络编程读书笔记 Facade Tester客户端门面模式的实例(被动版本化) 被动版本化,所以硬编码URL...
    melouverrr阅读 1,597评论 3 7
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,569评论 18 139
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,115评论 29 470
  • AFHTTPRequestOperationManager 网络传输协议UDP、TCP、Http、Socket、X...
    Carden阅读 4,312评论 0 12
  • iOS开发系列--网络开发 概览 大部分应用程序都或多或少会牵扯到网络开发,例如说新浪微博、微信等,这些应用本身可...
    lichengjin阅读 3,632评论 2 7