zoukankan      html  css  js  c++  java
  • ASIHTTPRequest学习(一)

    Creating a synchronous request

    可以创建同步和异步两种方式的请求,一般情况下应当使用异步请求。使用同步请求主应用线程会锁住直至解锁为止。

    • 创建异步请求,会在后台执行
    - (IBAction)grabURLInBackground:(id)sender
    {
       NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
       ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
       [request setDelegate:self];
       [request startAsynchronous];
    }
     
    - (void)requestFinished:(ASIHTTPRequest *)request
    {
       // Use when fetching text data
       NSString *responseString = [request responseString];
     
       // Use when fetching binary data
       NSData *responseData = [request responseData];
    }
     
    - (void)requestFailed:(ASIHTTPRequest *)request
    {
       NSError *error = [request error];
    }
    这里我们设置了代理,就可以接收状态了。
    • Using blocks

    - (IBAction)grabURLInBackground:(id)sender
    {
       NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
       __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
       [request setCompletionBlock:^{
          // Use when fetching text data
          NSString *responseString = [request responseString];
     
          // Use when fetching binary data
          NSData *responseData = [request responseData];
       }];
       [request setFailedBlock:^{
          NSError *error = [request error];
       }];
       [request startAsynchronous];
    }

    这里使用__block很重要,告诉block不要retain这个请求,可以防止retain-cycle因为request经常会retain block
    • Using a queue

    可以利用NSOperationQueue创建队列,当使用队列时在同一时间只有一定数量的请求可以执行。如果添加的请求多余队列的maxConcurrentOperationCount属性,那么其余的请求会等待其他请求执行完毕才会开始执行。
    - (IBAction)grabURLInTheBackground:(id)sender
    {
       if (![self queue]) {
          [self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
       }
     
       NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
       ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
       [request setDelegate:self];
       [request setDidFinishSelector:@selector(requestDone:)];
       [request setDidFailSelector:@selector(requestWentWrong:)];
       [[self queue] addOperation:request]; //queue is an NSOperationQueue
    }
     
    - (void)requestDone:(ASIHTTPRequest *)request
    {
       NSString *response = [request responseString];
    }
     
    - (void)requestWentWrong:(ASIHTTPRequest *)request
    {
       NSError *error = [request error];
    }
    上面自定义了selector requestDone requestWentWrong 如果不自定义的话那么默认的方法requestFinishedrequestFailed会被执行
    • About ASINetworkQueues

    ASINetworkQueue是NSOperationQueue的子类提供一些其他的功能:主要的功能是允许跟踪整个队列的上传和/或下载进度。
    此外,还提供一些额外的delegate方法:
      • requestDidStartSelector
        Called each time a request in the queue begins running. You can use this as an alternative to specifying a didStartSelector and setting a delegate on requests you add to the queue
      • requestDidReceiveResponseHeadersSelector
        Called each time a request in the queue receives response headers from the server. For large downloads, this may be some time before the request actually completes. You can use this as an alternative to specifying a didReceiveResponseHeadersSelector and setting a delegate on requests you add to the queue
      • requestDidFinishSelector
        Called each time a request in the queue completes successfully. You can use this as an alternative to specifying a didFinishSelector and setting a delegate on requests you add to the queue
      • requestDidFailSelector
        Called each time a request in the queue fails. You can use this as an alternative to specifying a didFailSelector and setting a delegate on requests you add to the queue
      • queueDidFinishSelector
        Called when the whole queue has completed, whether individual requests failed or succeeded.

    如果想使用上面的delegate就需要为queque而不是request来设置delegate了。ASINetworkQueues和NSOperationQueues是不同的,所有的请求不会立即执行。当使用ASINetworkQueues时,添加所有的请求,然后执行[queue go]当开始队列后,首先会执行head请求来获得要下载的数据总大小,一旦获得了总大小,就能真正显示总进度并开始真正的请求。

    当使用ASINetworkQueue来跟踪整个请求进度时,只有当请求开始运行时,才会在后退计算各个新请求的进度。当队列开始后再添加新的请求,那么ASINetworkQueue是不会执行head请求的,所以如果你为一个已经执行的队列添加多个请求,那么整体的进度是不会立即更新的。

    如果队列已经开启,那么就不需要再执行[queue go]

    当一个ASINetworkQueue中的请求失败了,那么默认的其他的请求都会cancel,如果想去掉这个功能,那么就设置[queue setShouldCancelAllRequestsOnFailure:NO].

    下面是一个例子来描述如何使用ASINetworkQueue

    http://gist.github.com/150447

    源代码如下:

    //
    // MyController.h
    //
    // Created by Ben Copsey on 20/07/2009.
    // Copyright 2009 All-Seeing Interactive. All rights reserved.
    //
     
    #import <Foundation/Foundation.h>
    #import <GHUnit/GHUnit.h>
    @class ASINetworkQueue;
     
    @interface MyController : NSObject {
    ASINetworkQueue *networkQueue;
     
    }
     
    - (void)doNetworkOperations;
     
    @property (retain) ASINetworkQueue *networkQueue;
     
    @end
     
    -------------------------------------------------------------------
     
    //
    // MyController.m
    //
    // Created by Ben Copsey on 20/07/2009.
    // Copyright 2009 All-Seeing Interactive. All rights reserved.
    //
     
    #import "MyController.h"
    #import "ASIHTTPRequest.h"
    #import "ASINetworkQueue.h"
     
    @implementation MyController
     
    - (void)dealloc
    {
    [networkQueue release];
    [super dealloc];
    }
     
    - (void)doNetworkOperations
    {
    // Stop anything already in the queue before removing it
    [[self networkQueue] cancelAllOperations];
     
    // Creating a new queue each time we use it means we don't have to worry about clearing delegates or resetting progress tracking
    [self setNetworkQueue:[ASINetworkQueue queue]];
    [[self networkQueue] setDelegate:self];
    [[self networkQueue] setRequestDidFinishSelector:@selector(requestFinished:)];
    [[self networkQueue] setRequestDidFailSelector:@selector(requestFailed:)];
    [[self networkQueue] setQueueDidFinishSelector:@selector(queueFinished:)];
     
    int i;
    for (i=0; i<5; i++) {
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com"]];
    [[self networkQueue] addOperation:request];
    }
     
    [[self networkQueue] go];
    }
     
    - (void)requestFinished:(ASIHTTPRequest *)request
    {
    // You could release the queue here if you wanted
    if ([[self networkQueue] requestsCount] == 0) {
     
    // Since this is a retained property, setting it to nil will release it
    // This is the safest way to handle releasing things - most of the time you only ever need to release in your accessors
    // And if you an Objective-C 2.0 property for the queue (as in this example) the accessor is generated automatically for you
    [self setNetworkQueue:nil];
    }
     
    //... Handle success
    NSLog(@"Request finished");
    }
     
    - (void)requestFailed:(ASIHTTPRequest *)request
    {
    // You could release the queue here if you wanted
    if ([[self networkQueue] requestsCount] == 0) {
    [self setNetworkQueue:nil];
    }
     
    //... Handle failure
    NSLog(@"Request failed");
    }
     
     
    - (void)queueFinished:(ASINetworkQueue *)queue
    {
    // You could release the queue here if you wanted
    if ([[self networkQueue] requestsCount] == 0) {
    [self setNetworkQueue:nil];
    }
    NSLog(@"Queue finished");
    }
     
    @synthesize networkQueue;
    @end
    • Handling success and failure for multiple requests in delegate methods

    如果想控制不同类型请求的成功和失败结果,可以有多个方法:
    1,如果请求的类型相同,想加以区分,那么就可以为每个请求设置userinfo 这个nsdictionary属性,为了简便起见,可以直接设置请求的tag属性来标记。userinfotag都不会发送给服务器,只供给本地使用。
    2,如果想为每个请求都单独设置各自的成功和失败操作,那么可以自定义方式来setDidFinishSelector / setDidFailSelector
    3, 如果是更为复杂的操作,那么可以为每个种类的请求分别写一个ASIHTTPRequest的子类来重写requestFinished: and failWithError:
    注意:这里需要避免使用url来作为区别不同请求的方式,因为这个url可能会随着重定向而改变。所以如果真的想使用url,那么就要使用[request originalURL]来代替。这个方法会始终使用原始的第一个url

    • Cancelling an asynchronous request

    使用[request cancel]方法来取消请求,但是不能cancel一个同步的请求

    如果你取消了一个请求,那么默认的会把这个动作视为error,就会调用failure delegate。所以,如果想避免这个调用,可以设置delegate为nil或者使用clearDelegatesAndCancel方法来替代

    当ASINetworkQueue中有一个request被cancel掉了,那么默认的其他的都会cancel,除非之前设置了shouldCancelAllRequestsOnFailure为no

    // Cancels an asynchronous request
    [request cancel]
     
    // Cancels an asynchronous request, clearing all delegates and blocks first
    [request clearDelegatesAndCancel];

    // When a request in this queue fails or is cancelled, other requests will continue to run
    [queue setShouldCancelAllRequestsOnFailure:NO];
     
    // Cancel all requests in a queue
    [queue cancelAllOperations];

    • Safely handling the delegate being deallocated before the request has finished

    // Ddealloc method for our controller
    - (void)dealloc
    {
       [request clearDelegatesAndCancel];
       [request release];
       ...
       [super dealloc];
    }
    • Sending data

    Setting request headers

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request addRequestHeader:@"Referer" value:@"http://allseeing-i.com/"];

    Sending a form POST with ASIFormDataRequest

    To send POST data in a manner compatible with web page forms, use the included ASIFormDataRequest subclass. Data is posted in ‘application/x-www-form-urlencoded’ format, or ‘multipart/form-data’ format when uploading binary data or files. Data in files is read as needed from disk, so POSTing large files is OK, as long as your web server is setup to handle them.

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request setPostValue:@"Ben" forKey:@"first_name"];
    [request setPostValue:@"Copsey" forKey:@"last_name"];
    [request setFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photo"];

    ASIFormDataRequest will autodetect the mime type of files (on iOS 3.0 or later an Mac) added to the POST via setFile:forKey:, and include this in the mime headers sent to the server. If you prefer, you can use the longer form to override this:

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
     
    // Upload a file on disk
    [request setFile:@"/Users/ben/Desktop/ben.jpg" withFileName:@"myphoto.jpg" andContentType:@"image/jpeg"
    forKey:@"photo"];
     
    // Upload an NSData instance
    [request setData:imageData withFileName:@"myphoto.jpg" andContentType:@"image/jpeg" forKey:@"photo"];
     

    You can send multiple values for the same parameter using the alternative add API:

    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request addPostValue:@"Ben" forKey:@"names"];
    [request addPostValue:@"George" forKey:@"names"];
    [request addFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photos"];
    [request addData:imageData withFileName:@"george.jpg" andContentType:@"image/jpeg" forKey:@"photos"];

    See ASIFormDataRequest.h for a full list of all the ways to add parameters to your POST.

    PUT requests and custom POSTs

    If you want to send data via PUT, or want to send it with POST but prefer to create the POST body yourself, use appendPostData: or appendPostDataFromFile:.

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request appendPostData:[@"This is my data" dataUsingEncoding:NSUTF8StringEncoding]];
    // Default becomes POST when you use appendPostData: / appendPostDataFromFile: / setPostBody:
    [request setRequestMethod:@"PUT"];

    If you want to send large amounts of data and aren’t using an ASIFormDataRequest, see the information on streaming post bodies from disk below.

    • Downloading data

    Downloading the response directly to a file

    1. 如果下载的资源过大,为了节省内存空间可以考虑直接下载到文件中去。这种方法将数据暂时存放再临时文件中,这个临时文件路径保存在temporaryFileDownloadPath中。当请求完成后,会发生:
    2. 如果数据是gzip压缩形式的(see information on gzip compression),那么会将文件解压缩到downloadDestinationPath中然后删除掉临时文件。
    3. 如果数据不是压缩形式的,那么临时文件会被移动到downloadDestinationPath中覆盖任何之前的文件。

    注意,如果response body是空的,那么就不会有文件被创建。所以最好先判断文件是否是空的然后再对其进行操作。

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

    Handling the response data as it arrives

    如果你需要在数据接收时进行一些操作,比如想一边接收一边用streaming parser来解析数据,那么就自己执行request:didReceiveData:。注意:当你这样执行后,ASIHTTPRequest不会产生responsedata或者将data写到downloadDestinationPath中去。你必须子阿吉存储文件。

    Reading the HTTP status code

    ASIHTTPRequest不会对http大多数的status codes做特殊处理,(除非重定向和认证status codes),所以需要你自己检查诸如404错误并保证之前的操作是对的。

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

    Reading response headers

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

    Handling text encodings

    ASIHTTPRequest will attempt to read the text encoding of the received data from the Content-Type header. If it finds a text encoding, it will set responseEncoding to the appropriate NSStringEncoding. If it does not find a text encoding in the header, it will use the value of defaultResponseEncoding (this defaults to NSISOLatin1StringEncoding).

    When you call [request responseString], ASIHTTPRequest will attempt to create a string from the data it received, using responseEncoding as the source encoding.

    Handling redirection

    ASIHTTPRequest will automatically redirect to a new URL when it encounters one of the following HTTP status codes, assuming a Location header was sent:

    • 301 Moved Permanently
    • 302 Found
    • 303 See Other

    When redirection occurs, the value of the response data (responseHeaders / responseCookies / responseData / responseString etc) will reflect the content received from the final location.

    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.

    You can turn off automatic redirection by setting the request’s shouldRedirect property to NO.

    By default, automatic redirects always redirect using a GET request (with no body). This behaviour matches the way most browsers work, not the HTTP spec, which specifies that 301 and 302 redirects should use the original method.

    To preserve the original method (including the request body) for 301 and 302 redirects, set shouldUseRFC2616RedirectBehaviour to YES on the request before you start it.

    Tracking progress

    每一个ASIHTTPRequest有两个代理来跟踪进度,downloadProgressDelegateuploadProgressDelegate

    进度代理可以是NSProgressIndicators (Mac OS X) or UIProgressViews (iPhone). 你也可以使用自定义的类作为progress delegate,只要他实现了setProgress:

    1. 如果正在执行单个request,那么就要设置upload 或/和 download progress delegate
    2. 如果正在执行多个request在队列中,那么就需要为整个队列设置progress
    3. 如果想同时为以上两种情况设置progress也是可以的

    IMPORTANT: If you are uploading to a site that requires authentication, the upload progress will be reset to its previous value every time it fails to supply valid authentication. For this reason, it is recommended that you only ever use an upload progress delegate when useSessionPersistence is YES (see below) when communicating with authenticating web servers, and ensure that you authenticate in another request before attempting to track the upload of a large amount of data.

    Tracking progress for uploads where the request body is less than 128KB is currently not possible. For requests larger than 128KB, progress delegates will not receive information on the progress of the first 128KB of post data. This is because of limitations in the CFNetwork API. I have filed a feature enhancement with Apple (bug id 6596016), hopefully they will modify CFNetwork in future to make this possible.

    Update 21st June 2009: The wonderful folks at Apple were kind enough to address my bug report! In the iPhone 3.0 SDK, it looks like the buffer size has been reduced to 32KB, which makes accurate upload progress tracking a lot more reliable.

    Tracking download progress for a single request

    In this example, myProgressIndicator is an NSProgressIndicator.

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

    Tracking download progress for a set of requests

    In this example, myProgressIndicator is an UIProgressView, myQueue is an 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]);
    }


    Tracking upload progress for a single request

    In this example, myProgressIndicator is an 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]);

    Tracking upload progress for a set of requests

    In this example, myProgressIndicator is an NSProgressIndicator, myQueue is an 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]);
    }

    Accurate progress vs simple progress

    ASIHTTPRequest provides two approaches for displaying progress, simple progress, and accurate progress. They are controlled using the showAccurateProgress of ASIHTTPRequests and ASINetworkQueues. When you set showAccurateProgress on a request, it affects only that request. When you set it on a queue, it affects all requests in the queue.

    Simple progress

    When using simple progress, progress will update only when a request completes. For a single request, this means you get two progress updates - 0% complete, and 100% complete. For a queue with four requests, you would get five progress updates, 0%, 25%, 50%, 75% and 100%, with each increment representing a request that has completed.

    Simple progress (showAccurateProgress = NO) is the default for ASINetworkQueues, and is well suited to queues with a large number of small uploads / downloads.

    Accurate progress

    When using accurate progress, progress will be updated as bytes are sent or received. It is best for requests that send or receive a large amount of data, and will give a much better indication of how much data has been sent or received for requests that take some time.

    Using accurate progress will slightly degrade the performance of uploads, since progress delegates (which are likely to be UIProgressViews or NSProgressIndicators) will be redrawn much more frequently.

    Using accurate progress has a much greater effect on performance for downloads when using a queue, since the queue will first perform a HEAD request for each GET request it contains so it can determine the overall size of the data to be downloaded before downloading commences. Using accurate progress is highly recommended if you are downloading large files from a queue, but you should avoid it for queues that contain a large number of small downloads.

    Accurate progress (showAccurateProgress == YES) is the default for ASIHTTPRequests that run synchronously.

    Custom progress tracking

    大多数情况下,设置uploadProgressDelegate and/or downloadProgressDelegate to an NSProgressIndicator or UIProgressView。如果想执行更为复杂的进度跟踪的话,delegates应该实现方法setProgress: (iOS) or setDoubleValue: / setMaxValue: (Mac).这些方法使你得到发送或接收时确切的字节数。

    Methods for downloadProgressDelegates

    • request:didReceiveBytes: will be called on your downloadProgressDelegate each time the request downloads a bit more data. (Note that this is distinct from the request:didReceiveData: method that you regular delegate may implement). 当有数据下载接收时就会调用到。
    • request:incrementDownloadSizeBy: will be called when the size of the download changes, with the passed parameter being the amount you should increase the size of the download by. This usually happens when the request receives response headers and finds out the size of the download. 当下载数据大小发生变化时调用此方法。参数就是需要增加的大小。

    Methods for uploadProgressDelegates

    • request:didSendBytes: will be called on your uploadProgressDelegate each time the request has been able to send a bit more data. Important: This method will be called with a number less than zero when a request needs to remove upload progress (usually when it has uploaded the data, but failed authentication or needs to run again for some other reason). 因为括号中的这些情况所以上传的大小可能是负数
    • request:incrementUploadSizeBy: will be called when the size of the upload changes. The passed size will frequently be below zero, as the request adjusts the upload size to take account of the size of the internal buffer used by the OS.
    • Handling HTTP authentication

    If you’re connecting to a server that requires authentication, you might want to take a look at this flowchart that shows how ASIHTTPRequest finds and applies credentials to requests.

    Specifying a username and password to use in the URL

    NSURL *url = [NSURL URLWithString:@"http://username:password@allseeing-i.com/top_secret/"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

    Setting a username and password to use on the request

    NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setUsername:@"username"];
    [request setPassword:@"password"];

    Storing credentials in the keychain

    If you turn on keychainPersistence, any valid username and password supplied will be stored in the keychain. Subsequent requests will reuse the username and password from the keychain, even if you quit and relaunch the application.

    NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setUseKeychainPersistence:YES];
    [request setUsername:@"username"];
    [request setPassword:@"password"];

    If you want to use the keychain but would rather manage it yourself, you might find the class methods relating to the keychain in ASIHTTPRequest.h helpful.

    Storing credentials in the session

    If useSessionPersistence is turned on (it is by default), ASIHTTPRequest stores credentials in memory and can re-use them for subsequent requests.

    NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com/top_secret/"];
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setUsername:@"username"];
    [request setPassword:@"password"];
    [request setUseSessionPersistence:YES]; //Shouldn't be needed as this is the default
     
    //Should reuse our username and password
    request = [ASIHTTPRequest requestWithURL:url];

    NTLM authentication

    To authenticate with a Windows server that uses the NTLM scheme, you also need to specify the domain you are authenticating against.

    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"];

    Using delegation to provide credentials

    Rather than specifying authentication credentials in advance, you might prefer to let each request ask its delegate for credentials if it can’t find them in the session authentication cache or keychain. This might be helpful if you want to connect to a server where you aren’t sure in advance what kind of authentication (if any) will be required.

    Make sure your delegate implements authenticationNeededForRequest:, and ASIHTTPRequest will pause a request while it waits for the delegate to let it know what credentials to use. When you have the credentials you need, simply set them on the request, and call [request retryUsingSuppliedCredentials];. If you want to cancel, you must call [request cancelAuthentication]. This will cancel the request.

    As of v1.0.8, request delegates will only receive one authenticationNeededForRequest: or proxyAuthenticationNeededForRequest: at once. Other requests requiring authentication will pause while the delegate handles the first request. If credentials are provided, any other requests currently in progress will attempt to reuse them, assuming they are valid for the url. If the delegate cancels authentication, and the queue’s shouldCancelAllRequestsOnFailure is YES, all the other requests will cancel without attempting to ask for credentials.

    You cannot use the delegation pattern for authentication when using synchronous requests.

    In older versions this would have caused your app to hang, as of v1.0.8 the delegate methods will no longer be called.

    Picture of the proxy authentication dialog

    Using the built-in authentication dialog (currently iOS only)

    New in v1.0.8 is the ASIAuthenticationDialog class. This is primarily used for working with authenticating proxies (see below), but it can be used to ask the user for credentials for authenticating webservers.

    For the best user experience, most apps that connect to a single service should implement authenticationNeededForRequest: in their request delegates, or avoid the use of delegation-style authentication altogether. However, there may be some occasions when the use of ASIHTTPRequest’s standard authentication dialog for regular authentication may be advantageous:

    • You don’t want to create your own login form
    • You may need to fetch data from an external source, and aren’t certain if it will require authentication or not

    For these cases, set shouldPresentAuthenticationDialog to true on your requests, and, if your delegate does not implement authenticationNeededForRequest:, users will see the dialog.

    Only one dialog can appear at once, and other requests requiring authentication will pause while the dialog is visible. If credentials are provided, any other requests currently in progress will attempt to reuse them, assuming they are valid for the url. If the delegate cancels authentication, and the queue’s shouldCancelAllRequestsOnFailure is YES (as per the default), all the other requests will cancel without attempting to ask for credentials.

    The authentication dialog will not appear for synchronous requests.

    The dialog is partly modelled on the authentication dialog that Safari uses on the iPhone, and includes:

    • A message to indicate these credentials are for a webserver (as opposed to a proxy)
    • The hostname or IP of the server you are connecting to
    • The authentication realm (if one was supplied)
    • Fields to enter a username and password
    • When connecting to a server that uses the NTLM scheme, the dialog also includes a field for the domain
    • A note about whether the credentials are sent in plain text or not (ie: They are sent in plain text only when using Basic authentication without SSL)

    If you want to change the look and feel, subclass ASIHTTPRequest, and override showAuthenticationDialog to show your custom dialog or ASIAuthenticationDialog subclass.

    Presenting credentials before the server asks for them

    IMPORTANT
    Behaviour for this feature has changed in v1.8.1 for requests using Basic authentication, and you may need to update your code.

    
    
    
    
    

    ASIHTTPRequest can present credentials to the server (if it has credentials to use) when it first makes a request, rather than waiting for the server to ask for credentials. This can result in better performance for applications using authentication, since it avoids an extra request.

    To trigger this behaviour for requests using Basic authentication, you should manually set the request’s authenticationScheme to use Basic:

    [request setAuthenticationScheme:(NSString *)kCFHTTPAuthenticationSchemeBasic];

    For other authentication schemes, credentials can be presented before the server asks for them, but only after another request has successfully authenticated with the server.

    You may wish to disable this feature if:

    • Your application may use multiple sets of credentials to talk to the same server at once
    • Security is paramount for your application. Using this feature is inherently less secure, since credentials are sent before you have a chance to validate that you are connecting to the server you thought you were connecting to.

    To disable this feature, use this code:

    [request setShouldPresentCredentialsBeforeChallenge:NO];

    Cookies

    Persistent cookies

    ASIHTTPRequest allows you to use the global store shared by all Mac OS X applications that use the CFNetwork or NSURLRequest APIs. If useCookiePersistence is on (it is by default), cookies will be stored in the shared NSHTTPCookieStorage container, and reused on other requests automatically. It’s worth noting that ASIHTTPRequest might present cookies created in other applications if they are valid for a particular request.

    You can clear all cookies created during a session like so:

    [ASIHTTPRequest setSessionCookies:nil];

    In this case, ‘session cookies’ refers to ALL cookies created during a session, rather cookies with no expiry date (often referred to as session cookies) that are removed when the application quits.

    Alternatively, the convenience class method clearSession will clear all cookies created during the session, along with any cached authentication data.

    Handling cookies yourself

    If you prefer, you can turn off useCookiePersistence, and manage the set of cookies for a particular request manually:

    //Create a cookie
    NSDictionary *properties = [[[NSMutableDictionary alloc] init] autorelease];
    [properties setValue:[@"Test Value" encodedCookieValue] forKey:NSHTTPCookieValue];
    [properties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName];
    [properties setValue:@".allseeing-i.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];
     
    //This url will return the value of the 'ASIHTTPRequestTestCookie' cookie
    url = [NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/read_cookie"];
    request = [ASIHTTPRequest requestWithURL:url];
    [request setUseCookiePersistence:NO];
    [request setRequestCookies:[NSMutableArray arrayWithObject:cookie]];
    [request startSynchronous];
     
    //Should be: I have 'Test Value' as the value of 'ASIHTTPRequestTestCookie'
    NSLog(@"%@",[request responseString]);
     
  • 相关阅读:
    程序员的自我救赎---1.4.3: 核心框架讲解(MVC)
    程序员的自我救赎---1.4.2: 核心框架讲解(BLL&Tool)
    程序员的自我救赎---1.4.1:核心框架讲解(DAL)
    程序员的自我救赎---1.3:事务的使用
    【零基础】极星9.5量化入门一:自定义套利的K线绘制
    【零基础】神经网络实践:BeatSaber粪谱生成器(使用方法篇)
    【零基础】使用Tensorflow实现神经网络
    【零基础】神经网络优化之Adam
    【零基础】神经网络优化之动量梯度下降
    【零基础】神经网络优化之mini-batch
  • 原文地址:https://www.cnblogs.com/lisa090818/p/3165250.html
Copyright © 2011-2022 走看看