从Cocoa向Tumblr发送POST请求

7
这段代码片段无法正常工作,服务器返回“身份验证失败”的响应。有什么想法吗?
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] 
                                    initWithURL:
                                    [NSURL URLWithString:@"http://www.tumblr.com/api/write"]];
    [request setHTTPMethod:@"POST"];
    [request addValue:_tumblrLogin forHTTPHeaderField:@"email"];
    [request addValue:_tumblrPassword forHTTPHeaderField:@"password"];
    [request addValue:@"regular" forHTTPHeaderField:@"type"];
    [request addValue:@"theTitle" forHTTPHeaderField:@"title"];
    [request addValue:@"theBody" forHTTPHeaderField:@"body"];

    NSLog(@"Tumblr Login:%@\nTumblr Password:%@", _tumblrLogin, _tumblrPassword);

    [NSURLConnection connectionWithRequest:request delegate:self];

    [request release];

_tumblrLogin_tumblrPassword都在我的代码的其他地方通过stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding进行处理。我的登录电子邮件的格式为“address+test@test.com”。直接登录Tumblr时可以正常工作,但我想知道“+”字符是否会导致编码问题?它没有被转义。需要转义吗?


感谢Martin的建议,我现在正在使用CFURLCreateStringByAddingPercentEscapes来转义我的登录名和密码。然而,我仍然遇到相同的问题,我的身份验证失败了。


请问是否有Tumblr API的可用工作代码参考? - AppAspect
2个回答

22
问题在于您没有创建一个正确的HTTP POST请求。POST请求需要一个格式正确的多部分MIME编码的主体,其中包含您要发送到服务器的所有参数。您正在尝试将参数设置为HTTP标头,这根本不起作用。
此代码将完成您想要的操作,请特别注意创建有效的多部分MIME字符串的 NSString 类别:
@interface NSString (MIMEAdditions)
+ (NSString*)MIMEBoundary;
+ (NSString*)multipartMIMEStringWithDictionary:(NSDictionary*)dict;
@end

@implementation NSString (MIMEAdditions)
//this returns a unique boundary which is used in constructing the multipart MIME body of the POST request
+ (NSString*)MIMEBoundary
{
    static NSString* MIMEBoundary = nil;
    if(!MIMEBoundary)
        MIMEBoundary = [[NSString alloc] initWithFormat:@"----_=_YourAppNameNoSpaces_%@_=_----",[[NSProcessInfo processInfo] globallyUniqueString]];
    return MIMEBoundary;
}
//this create a correctly structured multipart MIME body for the POST request from a dictionary
+ (NSString*)multipartMIMEStringWithDictionary:(NSDictionary*)dict 
{
    NSMutableString* result = [NSMutableString string];
    for (NSString* key in dict)
    {
        [result appendFormat:@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n",[NSString MIMEBoundary],key,[dict objectForKey:key]];
    }
    [result appendFormat:@"\r\n--%@--\r\n",[NSString MIMEBoundary]];
    return result;
}
@end


@implementation YourObject
- (void)postToTumblr
{
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] 
                                    initWithURL:
                                    [NSURL URLWithString:@"http://www.tumblr.com/api/write"]];
    [request setHTTPMethod:@"POST"];
    //tell the server to expect 8-bit encoded content as we're sending UTF-8 data, 
    //and UTF-8 is an 8-bit encoding
    [request addValue:@"8bit" forHTTPHeaderField:@"Content-Transfer-Encoding"];
    //set the content-type header to multipart MIME
    [request addValue: [NSString stringWithFormat:@"multipart/form-data; boundary=%@",[NSString MIMEBoundary]] forHTTPHeaderField: @"Content-Type"];

    //create a dictionary for all the fields you want to send in the POST request
    NSDictionary* postData = [NSDictionary dictionaryWithObjectsAndKeys:
                                 _tumblrLogin, @"email",
                                 _tumblrPassword, @"password",
                                 @"regular", @"type",
                                 @"theTitle", @"title",
                                 @"theBody", @"body",
                                 nil];
    //set the body of the POST request to the multipart MIME encoded dictionary
    [request setHTTPBody: [[NSString multipartMIMEStringWithDictionary: postData] dataUsingEncoding: NSUTF8StringEncoding]];
    NSLog(@"Tumblr Login:%@\nTumblr Password:%@", _tumblrLogin, _tumblrPassword);
    [NSURLConnection connectionWithRequest:request delegate:self];
    [request release];
}
@end

1
天啊,如果可以的话我愿意给这个点赞10000次。感谢你的帮助。顺便问一下,你有没有一个链接可以解释一下headerFields(为什么需要设置8位编码)和MIMEBoundary? - kubi
你需要设置8位编码,因为请求的主体是使用UTF8编码的数据块(使用NSString-dataUsingEncoding:方法并传入NSUTF8StringEncoding)。UTF8是一种8位编码,如果将其解释为7位编码(ASCII),则会导致数据损坏。多部分MIME编码允许您创建包含单独部分的字符串。每个部分由用作边界标记的常量字符串界定。边界字符串完全是任意的,但不能包含在字符串的非边界内容中。 - Rob Keniger
维基百科上关于MIME的文章可能会有所帮助:http://en.wikipedia.org/wiki/MIME#Multipart_messages - Rob Keniger

0
根据这个问题的答案,stringByAddingPercentEscapesUsingEncoding:并不执行完整的转义编码。出于某种原因,CoreFoundation版本的此方法确实执行了完整的转义编码:
[(NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, 
    (CFStringRef)[[self mutableCopy] autorelease], NULL, 
    CFSTR("=,!$&'()*+;@?\n\"<>#\t :/"), kCFStringEncodingUTF8) autorelease];

你也可以使用NSMutableString的replaceOccurrencesOfString:withString:options:方法手动进行替换,但是这种方法更加重复和冗长。(在这里查看.)

谢谢您的建议。我确实使用了CFURLCreate...,但它仍然无法正常工作。 - kubi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接