创建一个Connection
NSURLConnection对象有3种方式来获取URL的内容:同步,异步block回调和异步使用自定义的代理对象。
同步:使用sendSynchronousRequest:returningResponse:error:
方法来执行HTTP请求。
使用block回调:如果不需要获取request的状态,仅仅是当数据完全收到后做一些操作,可以使用sendAsynchronousRequest:queue:completionHandler:
方法。
使用代理:至少需要实现如下的方法connection:didReceiveResponse:
, connection:didReceiveData:
, connection:didFailWithError:
和connectionDidFinishLoading:
。支持的代理方法定义在NSURLConnectionDelegate, NSURLConnectionDownloadDelegate, 和 NSURLConnectionDataDelegate 协议中。
Listing 2-1 中创建一个connection。创建了一个NSURLRequest一个实例,指定缓存策略和连接的超时时间。然后,创建一个NSURLConnection实例,指定请求和代理。如果NSURLConnection不能为请求创建连接,initWithRequest:delegate:
返回nil。下面的代码片段也创建NSMutableData的实例来存储数据。
Listing 2-1 使用NSURLConnection创建连接
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData dataWithCapacity: 0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
// Release the receivedData object.
receivedData = nil;
// Inform the user that the connection failed.
}
一收到initWithRequest:delegate:
消息,传输就开始。在代理对象收到 connectionDidFinishLoading:
或者connection:didFailWithError:
消息之前可以被取消掉,通过给connection 发送 cancel
消息。
当服务器已提供了足够的数据来创建一个NSURLResponse对象时,代理对象就会收到connection:didReceiveResponse:
消息。代理方法可以通过检查NSURLResponse对象来获得数据预期的大小、MIME类型、建议的文件名和其他服务器提供的信息。
Listing 2-2 connection:didReceiveResponse:
的实现
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse object.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
当收到数据时代理对象被发送connection:didReceiveData
:消息。这个代理实现用来储存新收到的数据。
Listing 2-3 connection:didReceiveData:
的实现
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
你也可以使用connection:didReceiveData:
方法给用户提供一个连接进度指示器。首先在connection:didReceiveResponse:
方法中,URL response对象调用expectedContentLength
方法获取预期的长度。如果服务器未提供长度信息,expectedContentLength
会返回NSURLResponseUnknownLength
。
如果在传输的过程中发生了错误,connection:didFailWithError:
会被调用。传递的NSError对象参数会显示错误的细节。在user info
字典中NSURLErrorFailingURLStringErrorKey
键也提供了失败的URL。
Listing 2-4 connection:didFailWithError: 实现
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// Release the connection and the data object
// by setting the properties (declared elsewhere)
// to nil. Note that a real-world app usually
// requires the delegate to manage more than one
// connection at a time, so these lines would
// typically be replaced by code to iterate through
// whatever data structures you are using.
theConnection = nil;
receivedData = nil;
// inform the user
NSLog(@"Connection failed! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
最后,如果连接成功,调用connectionDidFinishLoading:
方法。
Listing 2-5 connectionDidFinishLoading:
实现
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a property elsewhere
NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
// Release the connection and the data object
// by setting the properties (declared elsewhere)
// to nil. Note that a real-world app usually
// requires the delegate to manage more than one
// connection at a time, so these lines would
// typically be replaced by code to iterate through
// whatever data structures you are using.
theConnection = nil;
receivedData = nil;
}
实现一个POST请求
使用initWithRequest:delegate:
方法创建 NSMutableURLRequest 对象.
需要构建body data,有三种情况:
- 对于上传短的、内存中得数据,你应该对数据进行URL编码,见Continuing Without Credentials
- 对于上传硬盘中的文件数据,调用
setHTTPBodyStream:
方法来告知NSMutableURLRequest从NSInputStream中读取数据,并把resulting data作为body content。 - 对于大块构造数据,调用 CFStreamCreateBoundPair 来创建一对数据流,然后调用
setHTTPBodyStream:
告诉 NSMutableURLRequest使用其中某一个stream作为它的body content。
Listing 2-6 配置了一个NSMutableRequest 对象用于POST请求
// In body data for the 'application/x-www-form-urlencoded' content type,
// form fields are separated by an ampersand. Note the absence of a
// leading ampersand.
NSString *bodyData = @"name=Jane+Doe&address=123+Main+St";
NSMutableURLRequest *postRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://www.apple.com"]];
// Set the request's content type to application/x-www-form-urlencoded
[postRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
// Designate the request a POST request and specify its body data
[postRequest setHTTPMethod:@"POST"];
[postRequest setHTTPBody:[NSData dataWithBytes:[bodyData UTF8String] length:strlen([bodyData UTF8String])]];
// Initialize the NSURLConnection and proceed as described in
// Retrieving the Contents of a URL
使用setValue:forHTTPHeaderField:
来给request指定不同的content type。
为了评估POST请求的进度,在代理方法中实现connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:
方法。