MPOAuthConnectionをPOSTとHTTPヘッダに対応させる
昨日の記事、『iPhone用TwitterクライアントでMPOAuthConnectionを使う』(id:nkmrshn:20090911)で、最後にMPOAuthConnectionをPOSTに対応させる方法、Google Code Archive - Long-term storage for Google Code Project Hosting.へのリンクを書きました。
これでもPOSTできるのですが、私はついでにHTTPヘッダを付加させてやりたいと思い、この方法は取らないことにしました。例えばTwitterのSearch APIでは、HTTPヘッダとしてUser-Agentが無いと、Rate limitingが通常より規制されるからです。
- http://apiwiki.twitter.com/Rate-limiting (Search API Rate Limiting)
Search API usage requires that applications include a unique and identifying User Agent string. A HTTP Referrer is expected but is not required. Consumers using the Search API but failing to include a User Agent string will receive a lower rate limit.
また、MPOAuthAPIクラスのperformMethodメソッドを呼び出す際、「POSTする/しない」と「HTTPヘッダ」を指定したかったというのもあります。そこで、以下のようにMPOAuthAPI.h/mおよびMPOAuthURLRequest.h/mにコードを追加・修正しました。
MPOAuthAPI.h
- (void)performMethod:(NSString *)inMethod atURL:(NSURL *)inURL withParameters:(NSArray *)inParameters withTarget:(id)inTarget andAction:(SEL)inAction doPost:(BOOL)inPost withHeaders:(NSDictionary *)inHeaders;
MPOAuthAPI.m
- (void)performMethod:(NSString *)inMethod withTarget:(id)inTarget andAction:(SEL)inAction { [self performMethod:inMethod atURL:self.baseURL withParameters:nil withTarget:inTarget andAction:inAction doPost:NO withHeaders:nil]; } - (void)performMethod:(NSString *)inMethod atURL:(NSURL *)inURL withParameters:(NSArray *)inParameters withTarget:(id)inTarget andAction:(SEL)inAction { [self performMethod:inMethod atURL:inURL withParameters:inParameters withTarget:inTarget andAction:inAction doPost:NO withHeaders:nil]; } - (void)performMethod:(NSString *)inMethod atURL:(NSURL *)inURL withParameters:(NSArray *)inParameters withTarget:(id)inTarget andAction:(SEL)inAction doPost:(BOOL)inPost withHeaders:(NSDictionary *)inHeaders { if (!inMethod && ![inURL path] && ![inURL query]) { [NSException raise:@"MPOAuthNilMethodRequestException" format:@"Nil was passed as the method to be performed on %@", inURL]; } NSURL *requestURL = inMethod ? [NSURL URLWithString:inMethod relativeToURL:inURL] : inURL; MPOAuthURLRequest *aRequest = [[MPOAuthURLRequest alloc] initWithURL:requestURL andParameters:inParameters doPost:inPost withHeaders:inHeaders];
MPOAuthURLRequest.h
@private NSURL *_url; NSString *_httpMethod; NSURLRequest *_urlRequest; NSMutableArray *_parameters; NSMutableDictionary *_headers; } @property (nonatomic, readwrite, retain) NSURL *url; @property (nonatomic, readwrite, retain) NSString *HTTPMethod; @property (nonatomic, readonly, retain) NSURLRequest *urlRequest; @property (nonatomic, readwrite, retain) NSMutableArray *parameters; @property (nonatomic, readwrite, retain) NSMutableDictionary *headers; - (id)initWithURL:(NSURL *)inURL andParameters:(NSArray *)inParameters; - (id)initWithURL:(NSURL *)inURL andParameters:(NSArray *)inParameters doPost:(BOOL)inPost withHeaders:(NSDictionary *)inHeaders;
MPOAuthURLRequest.m
- (id)initWithURL:(NSURL *)inURL andParameters:(NSArray *)inParameters { return [self initWithURL:inURL andParameters:inParameters doPost:NO withHeaders:nil]; } - (id)initWithURL:(NSURL *)inURL andParameters:(NSArray *)inParameters doPost:(BOOL)inPost withHeaders:(NSDictionary *)inHeaders { if (self = [super init]) { self.url = inURL; _parameters = inParameters ? [inParameters mutableCopy] : [[NSMutableArray alloc] initWithCapacity:10]; _headers = inHeaders ? [inHeaders mutableCopy] : nil; self.HTTPMethod = inPost ? @"POST" : @"GET"; } return self; } - (oneway void)dealloc { self.url = nil; self.HTTPMethod = nil; self.urlRequest = nil; self.parameters = nil; self.headers = nil; [super dealloc]; } @synthesize url = _url; @synthesize HTTPMethod = _httpMethod; @synthesize urlRequest = _urlRequest; @synthesize parameters = _parameters; @synthesize headers = _headers;
MPOAuthURLRequest.m (urlRequestSignedWithSecretメソッド内)
[aRequest setURL:self.url]; [aRequest setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"]; [aRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [aRequest setHTTPBody:postData]; } if(self.headers) { NSArray *keys = [self.headers allKeys]; for(NSString *key in keys) { [aRequest setValue:[self.headers objectForKey:key] forHTTPHeaderField:key]; MPLog(@"header - %@:%@", key, [aRequest valueForHTTPHeaderField:key]); } } [parameterString release]; [signatureParameter release];
実際に呼び出す例として、TwitterのSearch APIを以下のように書いてテストしてみました。MPOAuthのサンプルアプリ、MPOAuthMobileのRootViewController.xibに「Search」などというUIButtonを配置し、タップするとRootViewController.mのsearchButtonPressedメソットを呼びよう事前にInterface Builderで設定してあります。
- (void)_searchPost:(NSURL *)inURL withResponseString:(NSString *)inString { textOutput.text = inString; } - (IBAction)searchButtonPressed:(id)sender { NSArray *parameters = [MPURLRequestParameter parametersFromString:@"q=iPhone"]; NSString *userAgent = [NSString stringWithFormat:@"%@/%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"], [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]; NSDictionary *headers = [NSDictionary dictionaryWithObject:userAgent forKey:@"User-Agent"]; NSURL *url = [NSURL URLWithString:@"http://search.twitter.com/search.atom"]; [_oauthAPI performMethod:nil atURL:url withParameters:parameters withTarget:self andAction:@selector(_searchPost:withResponseString:) doPost:NO withHeaders:headers]; }
余談ですが、仕事ではMPOAuthConnectionを使っていません。理由は簡単で、MPOAuthConnectionの存在を昨日、偶然、知ったからです。