制作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类来下载,你的代码必须做到以下几条:
- 使用自定义的委托和你选择的配置对象来创建会话:
- 如果想在应用不运行的时候继续下载,你必须在的创建会话的时候提供后台会话配置对象(使用唯一的标识符)。
- 如果你不在意后台下载,你可以使用任何提供的会话配置对象类型来创建会话。
- 在会话中创建以及恢复一个或多个下载任务。
- 等待,直到你的委托接收到从任务或会话来的调用。具体来说,就是在文件下载完成的时候,必须实现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框架已弃用,不要用于新开发项目。