2011年3月3日

ODataObjCを使ったiOSアプリを作ってみた(2)~ODataObjCの使い方

ODataObjCを使ったiOSアプリを作ってみた(1)で紹介した「Cloud BookShelf」を作った際に、ODataObjCの使い方に関して色々ハマったところがあったのでメモ書き。
まず、ODataObjC付属のドキュメントに記載されたコードを見つつ、とりあえず必要そうな処理だけコピってやってみたところ、うまく動かなかった。Fiddler使ってHTTPリクエストの内容とかを見ていたところ、Azure REST APIで必要な署名が付与されておらず、認証エラーになっていた。
以下は付属ドキュメント上でエンティティをAzure Tableに保存するサンプルですが、
#import “Context/ObjectContext.h”
#define AZURE_SERVICE_URL “http://<Account>.table.core.windows.net”
#define AZURE_ACCOUNT_NAME “specify your account name here”
#define AZURE_ACCOUNT_KEY “specify your account key here”
 
@interface Person :TableEntry
{
        NSString *m_Name;
        NSString *m_Age;
}
 
@property(nonatomic,retain,getter=getName, setter = setName)NSString *m_Name;
@property(nonatomic,retain,getter=getAge, setter = setAge)NSString *m_Age;
 
-(id) initWithUri:(NSString *)aUri;
 
@end
 
@implementation Person
@synthesize m_Name,m_Age;
 
-(id) initWithUri:(NSString *)aUri
{
       self=[super initWithUri:aUri];
       return self;
}
@end
 
@try
{     
       AzureTableCredential *cred=[[AzureTableCredential alloc]
       initWithAccountName:AZURE_ACCOUNT_NAME accountKey:AZURE_ACCOUNT_KEY userPathStyleUrl:YES];
      
       ObjectContext *proxy=[[ObjectContext alloc]
       initWithUri:AZURE_SERVICE_URL credentials:cred dataServiceVersion:@"1.0"];
      
       [proxy setODataDelegate:self];
 
       Person *tableEntry=[[Person alloc]initWithUri:@""];
       [tableEntry setPartitionKey:@"Partition1"];
       [tableEntry setRowKey:@"Row1"];
       [tableEntry setName:@"TableEntry1"];
       [tableEntry setAge:@"37"];
             
       [proxy addObject:@"Person" object:tableEntry];//Person is the table name
       [proxy saveChanges];
}
      
@catch(NSException *e)
{
       NSLog(@"Exception:%@:%@",[e name],[e reason]);
}
このうち、必要ないと思って削っていた
[proxy setODataDelegate:self];
の部分が原因で、このライブラリではODataDelegateのonBeforeSendメソッド内で署名を付与するような設計になっているようです。まぁ必要ないと思って削ってしまった自分が悪いんですが。。。
しかしここで気になるのは、Azure Table用の署名の生成処理自体はユーティリティ(AzureTableUtil)として提供されているものの、このODataDelegateのonBeforeSendメソッドは自分で実装する必要があるという点。
こんな定型処理、なぜ自分で実装しないといけないんだ~
ということで、とりあえずこのObjectContextの生成と同時にAzure Table向けの署名を付けるようなクラスを作成する使い方がベストだと思います(多分ライブラリの作者もこんな使い方を想定してるはず)。
こんな感じ。
#import "Context/ObjectContext.h"
#import "Context/DataServiceQuery.h"
#import "Common/AzureTableUtil.h"
#import "Credential/AzureTableCredential.h"

#define TABLE_URL_FORMAT "http://%@.table.core.windows.net"

@interface StorageClient : NSObject<ODataDelegate, BlobContextDelegate> {
    NSString *tableServiceURL;
    NSString *accountName;
    NSString *accountKey;
}

@property(retain, nonatomic) NSString *tableServiceURL;
@property(retain, nonatomic) NSString *accountName;
@property(retain, nonatomic) NSString *accountKey;

- (id) initWithAccountName:(NSString *)aName andKey:(NSString *)aKey;
- (ObjectContext *) createTableContext;

@end


@implementation StorageClient

@synthesize tableServiceURL, accountName, accountKey, dateFormatter;

- (id) initWithAccountName:(NSString *)aName andKey:(NSString *)aKey {
    self.tableServiceURL = [NSString stringWithFormat:@TABLE_URL_FORMAT, aName];
    self.accountName = aName;
    self.accountKey = aKey;
    
    return self;
}

- (ObjectContext *) createTableContext {
    AzureTableCredential *cred = [[AzureTableCredential alloc] initWithAccountName:self.accountName accountKey:self.accountKey userPathStyleUrl:YES];
    ObjectContext *ctx = [[ObjectContext alloc] initWithUri:self.tableServiceURL credentials:cred dataServiceVersion:@"1.0"];
    
    [ctx setODataDelegate:self];
    
    return ctx;
}

- (void) onBeforeSend:(HttpRequest*)request{
    AzureTableUtil *util=[[AzureTableUtil alloc] initWithAccountName:self.accountName accountKey:self.accountKey usePathStyleUri:NO];
    [[request getHeaders] CopyFrom:[util getSignedHeaders:[request getUri]]];
}

@end

0 件のコメント:

コメントを投稿