一、下载数据
1.将服务器响应数据直接下载到文件
如果你请求的资源很大,你可以直接将数据下载到文件中来节省内存。此时,ASIHTTPRequest将不会一次将返回数据全部保持在内存中。
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadDestinationPath:@"/Users/ben/Desktop/my_file.txt"];
当我们使用downloadDestinationPath属性下载一个文件到本地时,数据将一直被保存在临时文件中直到请求完成。此时文件的路径名存储在temporaryFileDownloadPath中(如果不设置这个值,会自动生成一个文件名,在模拟器中,文件被创建在$TMPDIR中)。当request完成时,会发生下面两件事之一:- 如果数据是被压缩过(gzip)的,那么这个压缩过的文件将被解压到downloadDestinationPath,临时文件会被删除。
- 如果数据未被压缩,那么这个文件将被移动到downloadDestinationPath,冲突解决方式是:覆盖已存在的文件。
处理收到的服务器响应数据
如果你想处理服务器响应的数据(例如,你想使用流解析器对正在下载的数据流进行处理),你应该实现代理函数 request:didReceiveData(在ASIHTTPRequestDelegate.h中定义)。注意如果你这么做了,ASIHTTPRequest将不会填充responseData到内存,也不会将数据写入文件(downloadDestinationPath )——你必须自己搞定这两件事(之一)。2.获取HTTP状态码
ASIHTTPRequest并不对HTTP状态码做任何处理(除了重定向和授权状态码,下面会介绍到),所以你必须自己检查状态值并做出正确处理。ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
int statusCode = [request responseStatusCode];
NSString *statusMessage = [request responseStatusMessage];
3.读取响应头
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSString *poweredBy = [[request responseHeaders] objectForKey:@"X-Powered-By"];
NSString *contentType = [[request responseHeaders] objectForKey:@"Content-Type"];
4.处理文本编码
ASIHTTPRequest会试图从Content-Type头信息中读取返回数据的编码信息。- 如果它发现了编码信息,它会通过responseEncoding属性将编码信息设定为合适的 NSStringEncoding类型。
- 如果它没有找到编码信息,它会通过defaultResponseEncoding属性将编码设定为默认编码(NSISOLatin1StringEncoding)。
当你调用[request responseString],ASIHTTPRequest会尝试以responseEncoding将返回的Data转换为NSString。
301 Moved Permanently
302 Found
303 See Other
5.处理重定向
当遇到以下HTTP状态码之一时,ASIHTTPRequest会自动重定向到新的URL:301 Moved Permanently
302 Found
303 See Other
- 当发生重定向时,响应数据的值(responseHeaders,responseCookies,responseData,responseString等等)将会映射为最终地址的相应返回数据。
- 当URL发生循环重定向时,设置在这个URL上的cookie将被储存到全局域中,并在适当的时候随重定向的请求发送到服务器。
- 你可以关闭自动重定向:将shouldRedirect设置为NO。
- 默认情况下,自动重定向会使用GET请求(请求体为空)。这种行为符合大多数浏览器的行为,但是HTTP spec规定301和302重定向必须使用原有方法。
- 要对301、302重定向使用原方法(包含请求体),在发起请求之前,设置shouldUseRFC2616RedirectBehaviour 为YES。
二、进度追踪
每个ASIHTTPRequest有两个delegate用来追踪进度:
- downloadProgressDelegate 下载)
- uploadProgressDelegate (上载).
进度delegate可以是NSProgressIndicators (Mac OS X) 或者 UIProgressViews (iPhone),ASIHTTPRequest会自适应这两个class的行为。你也可以使用自定义class作为进度delegate,只要它响应setProgress:函数。
- 如果你执行单个request,那么你需要为该request设定upload/download进度delegate
- 如果你在进行多个请求,并且你想要追踪整个队列中的进度,你必须使用ASINetworkQueue并设置队列的进度delegate
- 如果上述两者你想同时拥有,恭喜你,0.97版以后的ASIHTTPRequest,这个可以有 ^ ^
如果你向一个要求身份验证的网站上传数据,那么每次授权失败,上传进度条就会被重置为上一次的进度值。因此,当与需要授权的web服务器交互时,建议仅当useSessionPersistence为YES时才使用上传进度条,并且确保你在追踪大量数据的上传进度之前,先使用另外的request来进行授权。
追踪小于128KB的数据上传进度目前无法做到,而对于大于128kb的数据,进度delegate不会收到第一个128kb数据块的进度信息。这是因为CFNetwork库API的限制。我们曾向apple提交过bug报告(bug id 6596016),希望apple能修改CFNetwork库以便实现上述功能。
追踪小于128KB的数据上传进度目前无法做到,而对于大于128kb的数据,进度delegate不会收到第一个128kb数据块的进度信息。这是因为CFNetwork库API的限制。我们曾向apple提交过bug报告(bug id 6596016),希望apple能修改CFNetwork库以便实现上述功能。
1.追踪单个request的下载进度
这个例子中, myProgressIndicator是个 NSProgressIndicator。
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadProgressDelegate:myProgressIndicator];//设定delegate,此处为下载进度,如果想实现上传进度,则改为upload
[request startSynchronous];
NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);//maxValue表示进度条最大值,doubleValue表示进度条当前值
2.追踪一系列request的下载进度
在这个例子中, myProgressIndicator 是个 UIProgressView,myQueue是个 ASINetworkQueue.- (void)fetchThisURLFiveTimes:(NSURL *)url
{
[myQueue cancelAllOperations];
[myQueue setDownloadProgressDelegate:myProgressIndicator];
[myQueue setDelegate:self];
[myQueue setRequestDidFinishSelector:@selector(queueComplete:)];
int i;
for (i=0; i<5; i++) {
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[myQueue addOperation:request];
}
[myQueue go];
}
- (void)queueComplete:(ASINetworkQueue *)queue
{
NSLog(@"Value: %f", [myProgressIndicator progress]);//progress是UIProgressView的属性
}
3.追踪单个request的上传进度
在这个例子中, myProgressIndicator 是个 UIProgressView
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"Ben" forKey:@"first_name"];
[request setPostValue:@"Copsey" forKey:@"last_name"];
[request setUploadProgressDelegate:myProgressIndicator];//这句话是启动上传进度追踪
[request startSynchronous];
NSLog(@"Value: %f",[myProgressIndicator progress]);//打印进度数值
注意:上面的代码最为重要的其实就是加了注释的两句话4.追踪一系列request的上传进度
这个例子中, myProgressIndicator是个 NSProgressIndicator, myQueue是个ASINetworkQueue.
- (void)uploadSomethingFiveTimes:(NSURL *)url
{
[myQueue cancelAllOperations];
[myQueue setUploadProgressDelegate:myProgressIndicator];
[myQueue setDelegate:self];
[myQueue setRequestDidFinishSelector:@selector(queueComplete:)];
int i;
for (i=0; i<5; i++) {
ASIHTTPRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostBody:[@"Some data" dataUsingEncoding:NSUTF8StringEncoding]];
[myQueue addOperation:request];
}
[myQueue go];
}
- (void)queueComplete:(ASINetworkQueue *)queue
{
NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
}
以上四个例子中,NSProgressIndicator和UIProgressView各用到了两个。其中UIProgressView用于iPhone项目,NSProgressIndicator用于Mac OS项目5.精确进度条vs简单进度条
ASIHTTPRequest提供两种进度条显示,简单进度条和精确进度条,通过使用ASIHTTPRequests 和ASINetworkQueues的showAccurateProgress属性来控制。showAccurateProgress = NO;//使用简单进度条
showAccurateProgress = YES;//使用精确进度条
- 为一个request设置showAccurateProgress只会对该request有效。
- 为一个队列设置showAccurateProgress,那么会影响队列里所有的request。
简单进度条
- 当使用简单进度条时,进度条只会在一个request完成时才更新。
- 对于单个request,这意味着你只有两个进度状态:0%和100%。
- 对于有5个request的队列来说,有五个状态:0%,25%,50%,75%,100%,每个request完成时,进度条增长一次。
- 简单进度条(showAccurateProgress = NO)是ASINetworkQueue的默认值,适用于大量小数据请求。
精确进度条
- 当使用精确进度条时,每当字节被上传或下载时,进度条都会更新。它适用于上传/下载大块数据的请求,并且会更好的显示已经发送/接收的数据量。
- 使用精确进度条追踪上传会轻微降低界面效率,因为进度delegate(一般是UIProgressViews 或NSProgressIndicators)会更频繁地重绘。
- 使用精确进度条追踪下载会更影响界面效率,因为队列会先为每个GET型request进行HEAD请求,以便统计总下载量。
- 强烈推荐对下载大文件的队列使用精确进度条,但是要避免对大量小数据请求使用精确进度条。
- 精确进度条(showAccurateProgress = YES)是以同步方式执行的ASIHTTPRequest的默认值。
6.自定义进度追踪
- 要想自定义进度追踪,必须使用ASIProgressDelegate这个协议,ASIProgressDelegate 协议定义了所有能更新一个request进度的方法。
- 多数情况下,设置你的uploadProgressDelegate或者 downloadProgressDelegate为 NSProgressIndicator或者UIProgressView会很好。
- 但是,如果你想进行更复杂的追踪,你的进度delegate实现下列函数要比 setProgress: (iOS) 或者 setDoubleValue: / setMaxValue: (Mac)好,这些函数允许你在实际量的数据被上传或下载时更新进度,而非简单方法的0到1之间的数字。
downloadProgressDelegates方法
request:didReceiveBytes: //每次request下载了更多数据时,这个函数会被调用(注意,这个函数与一般的代理实现的 request:didReceiveData:函数不同)。
request:incrementDownloadSizeBy: //当下载的文件大小发生改变时,这个函数会被调用,传入的参数是你需要增加的大小。这通常发生在request收到响应头并且找到下载大小时。
uploadProgressDelegates方法
request:didSendBytes: //每次request可以发送更多数据时,这个函数会被调用。注意:当一个request需要消除上传进度时(通常是该request发送了一段数据,但是因为授权失败或者其他什么原因导致这段数据需要重发)这个函数会被传入一个小于零的数字。
request:incrementUploadSizeBy://当上传的文件大小发生改变时,这个函数会被调用
三、身份验证
1.为URL指定要使用的用户名和密码
NSURL *url = [NSURL URLWithString:@"http://username:password@allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
2.为request指定要使用的用户名和密码
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];
3.将凭据存储到keychain
如果打开了keychainPersistence,所有提供的可用的用户名和密码将被存储到keychain中,以后的request将会重用这些用户名密码,即使你关闭程序后重新打开也不影响。NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUseKeychainPersistence:YES];//指定keychainPersistence为打开模式
[request setUsername:@"username"];
[request setPassword:@"password"];
如果你使用keychain但是想要自己管理它,你可以在ASIHTTPRequest.h文件里找到相关的类方法。4.将凭据存储到session中
如果打开了useSessionPersistence(默认即是如此),ASIHTTPRequest会把凭据存储到内存中,后来的request将会重用这些凭据。NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];
[request setUseSessionPersistence:YES]; //这句代码不是必需的,因为默认值就是YES
//Should reuse our username and password
request = [ASIHTTPRequest requestWithURL:url];
5.NTLM授权
要使用NTLM授权的Windows服务器,你还需要指定你要进行授权域。NSURL *url = [NSURL URLWithString:@"http://my.windows.server/top_secret/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setUsername:@"username"];
[request setPassword:@"password"];
[request setDomain:@"my-domain"];//设定授权域
6.使用代理来提供凭据
- 你不一定非要提前指定授权凭据,你还可以让每个request在无法从session或keychain中找到凭据时向它们的代理请求凭据。如果你要连接到一个你并不清楚授权类型的服务器时,这是很有用的。
- 你的delegate必须实现authenticationNeededForRequest:方法,当request等待凭据时,ASIHTTPRequest将会暂停这个request。
- 如果你持有你需要的凭据,那么先为request设定凭据,然后调用[request retryUsingSuppliedCredentials]即可。
- 如果你想取消授权,调用[request cancelAuthentication],此时,这个request也会被取消。
- 从1.0.8版开始,一次只能有一个request的delegate收到authenticationNeededForRequest: 或者 proxyAuthenticationNeededForRequest:。
- 当delegate处理第一个request时,其他需要授权的request将会被暂停。
- 如果提供了一个凭据,当前进程中所有其他的request将会假定这个凭据对这个URL有效,并尝试重用这个凭据。
- 如果delegate取消了授权,并且队列的shouldCancelAllRequestsOnFailure值为YES,所有其他的request都将被取消(它们也不会尝试请求凭据)。
- 当进行同步请求时,你不可以使用代理模式来授权。
7.使用内建的授权对话框(目前只对iOS有效)
- 这个特性归功于1.0.8版本的新类ASIAuthenticationDialog 。这个特性主要是用于授权代理(后面会介绍到),但是它也可以用来向用户取得授权凭据。
- 为了更好的用户体验,大多数(连接单一服务的)app必须为request的delegate实现authenticationNeededForRequest:方法,或者避免同时使用代理式授权。
但是,会有一些情况下,为普通的授权使用ASIHTTPRequest的标准授权对话框更好:
- 你不想创建你自己的登录表单
- 你可能需要从外部资源获取数据,但是你不清楚你需不需要进行授权
对于这些情况,为request设置shouldPresentAuthenticationDialog为YES,此时,如果你的代理没有实现authenticationNeededForRequest:方法,那么用户将会看到这个对话框。
- 一次同时只有一个对话框可以显示出来,所以当一个对话框显示时,所有其他需要授权的request将会暂停。
- 如果提供了一个凭据,当前进程中所有其他的request将会假定这个凭据对这个URL有效,并尝试重用这个凭据。
- 如果delegate取消了授权,并且队列的shouldCancelAllRequestsOnFailure值为YES,所有其他的request都将被取消(它们也不会尝试请求凭据)。
- 对于同步请求的request,授权对话框不会显示出来。
这个对话框部分模仿了iPhone上Safari使用的授权对话框,它包含以下内容:
- 一段信息来说明这些凭据是用于websever(而非一个proxy)
- 你将要连接到服务器的主机名或者IP
- 授权域(如果提供的话)
- 填写用户名和密码的区域
- 当连接到NTLM授权模式的服务器时,还会包含一个填写domain的区域
- 一个说明信息,指明凭据是否将会被以明文方式发送(例如:“只有当使用基于非SSL的基本授权模式时才会以明文方式发送”)
8.在服务器请求凭据前向服务器发送凭据
在第一次生成request时,ASIHTTPRequest可以先向服务器发送凭据(如果有的话),而不是等服务器要求提供凭据时才提供凭据。这个特性可以提高使用授权的程序的执行效率,因为这个特性避免了多余的request。a.对于基本授权模式,要触发这个行为,你必须手动设置request的authenticationScheme为kCFHTTPAuthenticationSchemeBasic:[request setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic];
b.对于其他授权方案,凭据也可以在服务器要求之前被发送,但是仅当有另一个request成功授权之后才行。在以下情况下,你也许想要禁用这个特性:
- 你的程序可能会一次使用一系列凭据来与服务器对话
- 安全性对于你的程序来说非常重要。使用这个特性是相对不安全的,因为你不能在凭据被发送前验证你是否连接到了正确的服务器。
[request setShouldPresentCredentialsBeforeChallenge:NO];