4

有一个比代码更多的是设计考虑的问题。

我的 iOS 应用程序与 json Web 服务接口。我正在使用 AFNetworking,我的问题基本上是我需要 init 函数(它验证 AFHTTPClient 并检索令牌)在我发出任何其他请求(需要所述令牌)之前完全完成。

从下面的代码中,我有兴趣了解实现这一目标的设计方法,我更愿意让所有请求保持异步,另一种解决方案是使 initWithHost:port:user:pass 中的请求同步(不使用 AFNetworking),我我知道这是不好的做法,并希望避免。

DCWebServiceManager.h

#import <Foundation/Foundation.h>
#import "AFHTTPClient.h"

@interface DCWebServiceManager : NSObject
{
    NSString *hostServer;
    NSString *hostPort;
    NSString *hostUser;
    NSString *hostPass;
    NSString *hostToken;
    AFHTTPClient *httpClient;
}

// Designated Initialiser
- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass;

// Instance Methods
- (void)getFileList;
@end

DCWebServiceManager.m

#import "DCWebServiceManager.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"

@implementation DCWebServiceManager

- (id)initWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass
{
    self = [super init];
    if (self)
    {
        hostServer = host;
        hostPort = port;
        hostUser = user;
        hostPass = pass;

        NSString *apiPath = [NSString stringWithFormat:@"http://%@:%@/", hostServer, hostPort];

        httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:apiPath]];
        [httpClient setAuthorizationHeaderWithUsername:hostUser password:hostPass];

        NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET" path:@"authenticate.php" parameters:nil];
        AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){

        // Do operations to parse request token to be used in
        // all requests going forward...
        // ...
        // ...
        // Results in setting: hostToken = '<PARSED_TOKEN>'        
            NSLog(@"HostToken: >>%@<<",  hostToken);

        } failure:^(AFHTTPRequestOperation *operation, NSError *error){
            NSLog(@"ERROR: %@",  operation.responseString);
        }];

        [operation start];
    }

    return self;
}

- (void)getFileList
{
    // *************************
    // The issue is here, getFileList gets called before the hostToken is retrieved..
    // Make the authenticate request in initWithHost:port:user:pass a synchronous request perhaps??
    // *************************
    NSLog(@"IN GETFILELIST: %@", hostToken); // Results in host token being nil!!!

    NSString *queryString = [NSString stringWithFormat:@"?list&token=%s", hostToken];
    NSMutableURLRequest *listRequest = [httpClient requestWithMethod:@"GET" path:queryString parameters:nil];

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:listRequest success:^(NSURLRequest *request,    NSHTTPURLResponse *response, id JSON){
        NSLog(@"SUCCESS!");
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
        NSLog(@"ERROR!: %@", error);
    }];

    [operation start];
}
@end

视图控制器.m

....
DCWebServiceManager *manager = [[DCWebServiceManager alloc] initWithHost:@"localhost" port:@"23312" user:@"FOO" pass:@"BAR"];
[manager getFileList];

// OUTPUTS
IN GETFILELIST: (nil)
HostToken: >>sdf5fdsfs46a6cawca6<<
....
...
4

2 回答 2

0

我建议继承 AFHTTPClient 并+sharedInstance为令牌添加一个 and 属性。

+ (MyGClient *)sharedInstanceWithHost:(NSString *)host port:(NSString *)port user:(NSString *)user pass:(NSString *)pass {
    static MyClient *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [... your code from the init ...]
    });
    return sharedInstance;
}

然后,您可以在对进一步操作进行排队之前覆盖enqueueHTTPRequestOperationWithRequest:success:failure以检查令牌。

此外,您可以通过覆盖属性的设置器来收集操作并在设置令牌后立即将它们排入队列。

于 2013-02-22T13:34:32.587 回答
0

就像@patric.schenke 所说,AFHTTPClient如果你想清理一些代码,你可以子类化,但真正的问题是你需要在向getFileList.

我建议以与AFNetworking使用块保持异步相同的方式使用块。将您的 HTTP 调用移动到它自己的方法中,并且仅在您hostToken为 nil 时调用它:

- (void)getFileList
{
  if (self.token == nil) {
    [self updateTokenThenWhenComplete:^(void){
      // make HTTP call to get file list
    }];
  } else {
    // make HTTP call to get file list
  }
}

- (void)updateTokenThenWhenComplete:(void (^))callback
{
  //... make HTTP request
  [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject){
      self.token = responseObject.token;
      callback();
    } failure:^(AFHTTPRequestOperation *operation, NSError *error){
        //...
    }];

}
于 2013-04-12T22:21:57.997 回答