• ASIHTTPRequest总结


    创建和执行request

    同步请求

    同步请求会在当前线程中执行,使用error属性来检查结束状态(要下载大文件,则需要设定downloadDestinationPath来保存文件到本地)。

      NSURL *url = [NSURL URLWithString:movieURL];
      ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
      [request startSynchronous];
      NSError *error = [request error];
      if (!error) {
        NSString *response = [request responseString];
      }
    

     同步请求会阻塞主线程的执行,这导致用户界面不响应用户操作,任何动画都会停止渲染。

    异步请求

    下面是最简单的异步请求方法,这个request会在全局的NSOperationQueue中执行

        //请求类
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:movieURL]];
        request.delegate = self;
        //异步发送请求
        [request startAsynchronous];
    

     在两个委托函数中处理结果

    #pragma mark - ASIHTTPRequestDelegate
    - (void)requestFinished:(ASIHTTPRequest *)request{
        NSError *error;
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:request.responseData options:kNilOptions error:&error];
        NSArray *movieArray = [jsonDict objectForKey:@"subjects"];
    
        for (NSDictionary *dict  in movieArray)
        {
            SYMovieModel *model = [[SYMovieModel alloc] init];
            model.movieName = dict[@"title"];
            model.movieYear = dict[@"year"];
            model.movieImage = dict[@"images"][@"large"];
            
            [_saveDataArray addObject:model];
        }
        //刷新表
        [_tableView reloadData];
    }
    
    - (void)requestFailed:(ASIHTTPRequest *)request{
        NSLog(@"请求失败");
    }

    使用block

    在平台支持情况下,ASIHTTPRequest1.8以上支持block:setCompletionBlock和setFailedBlock:

    - (void)grabURLInBackground{
        NSURL *url = [NSURL URLWithString:movieURL];
        __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
        [request setCompletionBlock:^{
            NSError *error;
            NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:request.responseData options:kNilOptions error:&error];
            NSArray *movieArray = [jsonDict objectForKey:@"subjects"];
    
            for (NSDictionary *dict  in movieArray)
            {
                SYMovieModel *model = [[SYMovieModel alloc] init];
                model.movieName = dict[@"title"];
                model.movieYear = dict[@"year"];
                model.movieImage = dict[@"images"][@"large"];
                
                [_saveDataArray addObject:model];
            }
            //刷新表
            [_tableView reloadData];
        }];
        [request setFailedBlock:^{
            NSError *error = [request error];
        }];
        [request startAsynchronous];
    }
    

     (注意,声明request时要使用__block修饰符,这是为了告诉block不要retain request,以免出现retain循环,因为request是会retain block的。)

    使用NSOperationQueue

     创建NSOperationQueue或者ASINetworkQueue队列,我们还可以设定最大并发连接数:maxConcurrentOperationCount

    - (void)grabURLInBackground{
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        queue.maxConcurrentOperationCount = 4;
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:movieURL]];
        [request setDelegate:self];
        [request setDidFinishSelector:@selector(requestDone:)];
        [request setDidFailSelector:@selector(requestWentWrong:)];
        [queue addOperation:request];
    }
    
    - (void)requestDone:(ASIHTTPRequest *)request
    {
        NSError *error;
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:request.responseData options:kNilOptions error:&error];
        NSArray *movieArray = [jsonDict objectForKey:@"subjects"];
    }
    
    - (void)requestWentWrong:(ASIHTTPRequest *)request
    {
        NSError *error = [request error];
    }

    如果不设定selector,那么系统会使用默认的requestFinished: 和 requestFailed:方法

    如果需要对队列里面的每个request进行区分,那么可以设定request的userInfo属性,它是个NSDictionary,或者更简单的方法是设定每个request的tag属性,这两个属性都不会被发送到服务器。

    不要使用request的URL来区分每个request,因为URL可能会改变(例如重定向),如果需要使用request的URL,使用[request originalURL],这个将永远返回第一个url。

    使用ASINetworkQueue

    ASINetworkQueue是NSOperationQueue的子类,提供更高级的特性(ASINetworkQueue的代理函数):

    • requestDidStartSelector
      当一个request开始执行时,这个代理函数会被调用。
    • requestDidReceiveResponseHeadersSelector
      当队列中的request收到服务器返回的头信息时,这个代理函数会被调用。对于下载很大的文件,这个通常比整个request的完成要早。
    • requestDidFinishSelector
      当每个request完成时,这个代理函数会被调用。
    • requestDidFailSelector
      当每个request失败时,这个代理函数会被调用。
    • queueDidFinishSelector
      当队列完成(无论request失败还是成功)时,这个代理函数会被调用。

    ASINetworkQueues与NSOperationQueues稍有不同,加入队列的request不会立即开始执行。如果队列打开了进度开关,那么队列开始时,会先对所有GET型request进行一次HEAD请求,获得总下载大小,然后真正的request才被执行。

    向一个已经开始进行的ASINetworkQueue 加入request会怎样?

    如果你使用ASINetworkQueue来跟踪若干request的进度,只有当新的request开始执行时,总进度才会进行自适应调整(向后移动)。ASINetworkQueue不会为队列开始后才加入的request进行HEAD请求,所以如果你一次向一个正在执行的队列加入很多request,那么总进度不会立即被更新。

    如果队列已经开始了,不需要再次调用[queue go]

    当ASINetworkQueue中的一个request失败时,默认情况下,ASINetworkQueue会取消所有其他的request。要禁用这个特性,设置 [queue setShouldCancelAllRequestsOnFailure:NO]

    ASINetworkQueues只可以执行ASIHTTPRequest操作,二不可以用于通用操作。试图加入一个不是ASIHTTPRequest的NSOperation将会导致抛出错误。

    取消异步请求

    取消一个异步请求(无论request是由[request startAsynchronous]开始的还是从你创建的队列中开始的),使用[request cancel]即可。注意同步请求不可以被取消。

    注意,如果你取消了一个request,那么这个request将会被视为请求失败,并且request的代理或者队列的代理的失败代理函数将被调用。如果你不想让代理函数被调用,那么将delegate设置为nil,或者使用clearDelegatesAndCancel方法来取消request。

    clearDelegatesAndCancel 将会首先清除所有的代理和block。

    当使用ASINetworkQueue时,如果取消了队列中的一个request,那么队列中其他所有request都会被取消,可以设置shouldCancelAllRequestsOnFailure的值为NO来避免这个现象。

    安全地控制delegate防止request完成之前代理被释放

    request并不retain它们的代理,所以有可能你已经释放了代理,而之后request完成了,这将会引起崩溃。大多数情况下,如果你的代理即将被释放,你一定也希望取消所有request,因为你已经不再关心它们的返回情况了。如此做:[request clearDelegatesAndCancel];

    (如果使用block是不是无此问题?)

    发送数据

    设定request头

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request addRequestHeader:@"Referer" value:@"http://www.dreamingwish.com/"];
    

    使用ASIFormDataRequest POST表单

    PUT请求、自定义POST请求

    如果你想发送PUT请求,或者你想自定义POST请求,使用appendPostData: 或者 appendPostDataFromFile:

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request appendPostData:[@"This is my data" dataUsingEncoding:NSUTF8StringEncoding]];
    [request setRequestMethod:@"PUT"];
    

    下载数据

    将服务器响应数据直接下载到文件

    如果你请求的资源很大,你可以直接将数据下载到文件中来节省内存。此时,ASIHTTPRequest将不会一次将返回数据全部保持在内存中。

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDownloadDestinationPath:@"/Users/ben/Desktop/my_file.txt"];

    当我们把数据下载到downloadDestinationPath时,数据将首先被存在临时文件中。此时文件的路径名存储在temporaryFileDownloadPath中(梦维:如果不设置这个值,会自动生成一个文件名,在模拟器中,文件被创建在$TMPDIR中)。当request完成时,会发生下面两件事之一:

    • 如果数据是被压缩过(gzip)的,那么这个压缩过的文件将被解压到downloadDestinationPath,临时文件会被删除。
    • 如果数据未被压缩,那么这个文件将被移动到downloadDestinationPath,冲突解决方式是:覆盖已存在的文件。

    注意,如果服务器响应数据为空,那么文件是不会被创建的。如果你的返回数据可能为空,那么你应该先检查下载文件是否存在,再对文件进行操作。

    处理收到的服务器响应数据

    如果你想处理服务器响应的数据(例如,你想使用流解析器对正在下载的数据流进行处理),你应该实现代理函数 request:didReceiveData:。注意如果你这么做了,ASIHTTPRequest将不会填充responseData到内存,也不会将数据写入文件(downloadDestinationPath )——你必须自己搞定这两件事(之一)。

    获取HTTP状态码:[request responseStatusCode]

    ASIHTTPRequest并不对HTTP状态码做任何处理(除了重定向和授权状态码,下面会介绍到),所以你必须自己检查状态值并正确处理。

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request startSynchronous];
    int statusCode = [request responseStatusCode];
    NSString *statusMessage = [request responseStatusMessage];
    

    读取响应头:[request responseHeaders]

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request startSynchronous];
    NSString *poweredBy = [[request responseHeaders] objectForKey:@"X-Powered-By"];
    NSString *contentType = [[request responseHeaders] objectForKey:@"Content-Type"];
    

    处理文本编码

    ASIHTTPRequest会试图读取返回数据的编码信息(Content-Type头信息)。如果它发现了编码信息,它会将编码信息设定为合适的 NSStringEncoding.如果它没有找到编码信息,它会将编码设定为默认编码(NSISOLatin1StringEncoding)

    当你调用[request responseString],ASIHTTPRequest会尝试以responseEncoding将返回的Data转换为NSString。

    处理重定向

    当遇到以下HTTP状态码之一时,ASIHTTPRequest会自动重定向到新的URL

    • 301 Moved Permanently
    • 302 Found
    • 303 See Other

    当发生重定向时,响应数据的值(responseHeaders,responseCookies,responseData,responseString等等)将会映射为最终地址的相应返回数据。

    当URL发生循环重定向时,设置在这个URL上的cookie将被储存到全局域中,并在适当的时候随重定向的请求发送到服务器。

    Cookies set on any of the urls encountered during a redirection cycle will be stored in the global cookie store, and will be represented to the server on the redirected request when appropriate.

    你可以关闭自动重定向:将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,这个可以有 ^_^

    IMPORTANT:如果你向一个要求身份验证的网站上传数据,那么每次授权失败,上传进度条就会被重置为上一次的进度值。因此,当与需要授权的web服务器交互时,建议仅当useSessionPersistence为YES时才使用上传进度条,并且确保你在追踪大量数据的上传进度之前,先使用另外的request来进行授权。 

    追踪小于128KB的数据上传进度目前无法做到,而对于大于128kb的数据,进度delegate不会收到第一个128kb数据块的进度信息。这是因为CFNetwork库API的限制。我们曾向apple提交过bug报告(bug id 6596016),希望apple能修改CFNetwork库以便实现上述功能。

    2009-6-21:Apple的哥们儿们真棒!iPhone 3.0 SDK里,buffer大小已经被减小到32KB了,我们的上传进度条可以更精确了。

     

    追踪单个request的下载进度

    这个例子中, myProgressIndicator是个 NSProgressIndicator:

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDownloadProgressDelegate:myProgressIndicator];
    [request startSynchronous];
    NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
    

    追踪一系列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];
    }
    

    追踪单个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]);
    

    追踪一系列request的上传进度

    [myQueue setUploadProgressDelegate:myProgressIndicator];
    

    精确进度条vs简单进度条

    ASIHTTPRequest提供两种进度条显示,简单进度条和精确进度条,使用ASIHTTPRequests 和ASINetworkQueues的showAccurateProgress 来控制。为一个request设置showAccurateProgress只会对该request有效。如果你为一个队列设置showAccurateProgress,那么会影响队列里所有的request。

    1.简单进度条

    当使用简单进度条时,进度条只会在一个request完成时才更新。对于单个request,这意味着你只有两个进度状态:0%和100%。对于一个有5个request的队列来说,有五个状态:0%,25%,50%,75%,100%,每个request完成时,进度条增长一次。

    简单进度条(showAccurateProgress = NO)是ASINetworkQueue的默认值,适用于大量小数据请求。

    2.精确进度条

    当使用精确进度条时,每当字节被上传或下载时,进度条都会更新。它适用于上传/下载大块数据的请求,并且会更好的显示已经发送/接收的数据量。

    使用精确进度条追踪上传会轻微降低界面效率,因为进度delegate(一般是UIProgressViews 或NSProgressIndicators)会更频繁地重绘。

    使用精确进度条追踪下载会更影响界面效率,因为队列会先为每个GET型request进行HEAD请求,以便统计总下载量强烈推荐对下载大文件的队列使用精确进度条,但是要避免对大量小数据请求使用精确进度条

    精确进度条(showAccurateProgress = YES)是以同步方式执行的ASIHTTPRequest的默认值。

    自定义进度追踪

    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发送了一段数据,但是因为授权失败或者其他什么原因导致这段数据需要重发)这个函数会被传入一个小于零的数字。

    身份验证

    Cookie的使用

    持久化cookie

    ASIHTTPRequest允许你使用全局存储来和所有使用CFNetwork或者NSURLRequest接口的程序共享cookie。

    如果设置useCookiePersistence为YES(默认值),cookie会被存储在共享的 NSHTTPCookieStorage 容器中,并且会自动被其他request重用。值得一提的是,ASIHTTPRequest会向服务器发送其他程序创建的cookie(如果这些cookie对特定request有效的话)。

    你可以清空session期间创建的所有cookie:

    [ASIHTTPRequest setSessionCookies:nil];

    这里的‘session cookies’指的是一个session中创建的所有cookie,而非没有过期时间的cookie(即通常所指的会话cookie,这种cookie会在程序结束时被清除)。

    另外,有个方便的函数 clearSession可以清除session期间产生的所有的cookie和缓存的授权数据。 

    自己处理cookie

    如果你愿意,你大可以关闭useCookiePersistence,自己来管理某个request的一系列cookie:(cookie的内容?)

    NSDictionary *properties = [[[NSMutableDictionary alloc] init] autorelease];
    [properties setValue:[@"Test Value" encodedCookieValue] forKey:NSHTTPCookieValue];
    [properties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName];
    [properties setValue:@".dreamingwish.com" forKey:NSHTTPCookieDomain];
    [properties setValue:[NSDate dateWithTimeIntervalSinceNow:60*60] forKey:NSHTTPCookieExpires];
    [properties setValue:@"/asi-http-request/tests" forKey:NSHTTPCookiePath];
    NSHTTPCookie *cookie = [[[NSHTTPCookie alloc] initWithProperties:properties] autorelease];
     
    //这个url会返回名为'ASIHTTPRequestTestCookie'的cookie的值
    url = [NSURL URLWithString:@"http://www.dreamingwish.com/"];
    request = [ASIHTTPRequest requestWithURL:url];
    [request setUseCookiePersistence:NO];
    [request setRequestCookies:[NSMutableArray arrayWithObject:cookie]];
    [request startSynchronous];
     
    //将会打印: I have 'Test Value' as the value of 'ASIHTTPRequestTestCookie'
    NSLog(@"%@",[request responseString]);
    

    数据压缩

    使用gzip来处理压缩的响应数据

    从0.9版本开始,ASIHTTPRequest会提示服务器它可以接收gzip压缩过的数据。

    许多web服务器可以在数据被发送之前压缩这些数据——这可以加快下载速度减少流量使用,但会让服务器的cpu(压缩数据)和客户端(解压数据)付出代价。总的来说,只有特定的几种数据会被压缩——许多二进制格式的文件像jpeg,gif,png,swf和pdf已经压缩过他们的数据了,所以向客户端发送这些数据时不会进行gzip压缩。文本文件例如网页和xml文件会被压缩,因为它们通常有大量的数据冗余。

    怎样设置apache的mod_deflate来使用gzip压缩数据

    apache 2.x以上版本已经配备了mod_deflate扩展,这使得apache可以透明地压缩特定种类的数据。要开启这个特性,你需要在apache的配置文件中启用mod_deflate。并将mod_deflate命令添加到你的虚拟主机配置或者.htaccess文件中。

    在ASIHTTPRequest中使用gzip

    - (IBAction)grabURL:(id)sender{
      NSURL *url = [NSURL URLWithString:@"http://www.dreamingwish.com"];
      ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
      // 默认为YES, 你可以设定它为NO来禁用gzip压缩
      [request setAllowCompressedResponse:YES];
      [request startSynchronous];
      BOOL *dataWasCompressed = [request isResponseCompressed]; // 响应是否被gzip压缩过?
      NSData *compressedResponse = [request rawResponseData]; // 压缩的数据
      NSData *uncompressedData = [request responseData]; // 解压缩后的数据
      NSString *response = [request responseString]; // 解压缩后的字符串
    }
    

    当allowCompressedResponse 设置为YES时,ASIHTTPRequest将向request中增加一个Accept-Encoding头,表示我们可以接收gzip压缩过的数据。如果响应头中包含一个Content-Encoding头指明数据是压缩过的,那么调用responseData 或者responseString 将会得到解压缩后的数据。你也可以通过调用rawResponseData来获得原始未压缩的数据。

    相应数据的实时解压缩

    默认情况下,ASIHTTPRequest会等到request完成时才解压缩返回的数据。若设置request的shouldWaitToInflateCompressedResponses 属性为NO,ASIHTTPRequest将会对收到的数据进行实时解压缩。 在某些情况下,这会稍稍提升速度,因为数据可以在reqeust等待网络数据时进行处理。

    如果你需要对响应数据流进行流处理(例如XML和JSON解析),这个特性会很有用。如果启用了这个选项,你可以通过实现代理函数request:didReceiveData:来将返回的网络数据一点一点喂给解析器。

    注意,如果shouldWaitToInflateCompressedResponses 被设置为NO,那么原始(未解压)的数据会被抛弃。具体情况请查阅ASIHTTPRequest.h的代码注释。

    使用gzip压缩request数据

    1.0.3版本的新特性就是gzip压缩request数据。使用这个特性,你可以通过设置shouldCompressRequestBody 为YES来使你的程序压缩POST/PUT的内容,默认值为NO。

    apache的mod_deflate可以自动解压缩gzip压缩的请求体(通过合适的设置)。这个方法适用于CGI内容,但不适用于内容过滤器式的模块(例如mod PHP),这种情况下,你就必须自己解压缩数据。

    ASIHTTPRequest 无法检测一个服务器是否能接收压缩过的请求体。当你确定服务器可以解压缩gzip包时,再使用这个特性。

    请避免对已经压缩过的格式(例如jpeg/png/gif/pdf/swf)进行压缩,你会发现压缩后的数据比原数据更大。(梦维:因为压缩包都有头信息)

  • 相关阅读:
    4.关于QT中的QFile文件操作,QBuffer,Label上加入QPixmap,QByteArray和QString之间的差别,QTextStream和QDataStream的差别,QT内存映射(
    PlSql加入数据库链接
    UserScan的处理流程分析
    第八十八题(金山笔试题,字符串移动)
    4Sum_leetCode
    LeetCode Add Binary
    Hibernate or JPA Annotation中BLOB、CLOB注解写法
    配置Nginx防止直接用IP訪问Webserver
    Java集合系列之TreeMap源代码分析
    使用Application Loader上传APP流程解读[APP公布]
  • 原文地址:https://www.cnblogs.com/zhongriqianqian/p/4135449.html
Copyright © 2020-2023  润新知