From acd6e492943d739efc0fdf76b900f8f8d40f68f6 Mon Sep 17 00:00:00 2001 From: Mattt Thompson Date: Mon, 19 Mar 2012 12:44:06 -0400 Subject: [PATCH] [Issue #243] Fixing AFQueryStringFromParametersWithEncoding to encode nested array values correctly --- AFNetworking/AFHTTPClient.m | 132 ++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/AFNetworking/AFHTTPClient.m b/AFNetworking/AFHTTPClient.m index 42ad17a..a468a4c 100644 --- a/AFNetworking/AFHTTPClient.m +++ b/AFNetworking/AFHTTPClient.m @@ -96,7 +96,7 @@ static NSString * AFBase64EncodedStringFromString(NSString *string) { } NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { - static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+,:;='\"`<>()[]{}/\\|~ "; + static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ "; /* The documentation for `CFURLCreateStringByAddingPercentEscapes` suggests that one should "pre-process" URL strings with unpredictable sequences that may already contain percent escapes. However, if the string contains an unescaped sequence with '%' appearing without an escape code (such as when representing percentages like "42%"), `stringByReplacingPercentEscapesUsingEncoding` will return `nil`. Thus, the string is only unescaped if there are no invalid percent-escaped sequences. @@ -109,61 +109,101 @@ NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEn return [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding)) autorelease]; } -extern NSDictionary * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey); -extern NSDictionary * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey); -extern NSDictionary * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey); -extern NSDictionary * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key); +#pragma mark - -NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding) { - NSMutableArray *queryStringArray = [NSMutableArray array]; - - [AFQueryParametersFromParametersAtBaseKeyWithEncoding(parameters, nil) enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { - [queryStringArray addObject:[NSString stringWithFormat:@"%@=%@", AFURLEncodedStringFromStringWithEncoding([key description], encoding), AFURLEncodedStringFromStringWithEncoding([obj description], encoding)]]; - }]; - - return [queryStringArray componentsJoinedByString:@"&"]; +@interface AFQueryStringComponent : NSObject { +@private + NSString *_key; + NSString *_value; } -NSDictionary * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey) { - NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; +@property (readwrite, nonatomic, retain) id key; +@property (readwrite, nonatomic, retain) id value; + +- (id)initWithKey:(NSString *)key value:(NSString *)value; + +@end + +@implementation AFQueryStringComponent +@synthesize key = _key; +@synthesize value = _value; + +- (id)initWithKey:(NSString *)key value:(NSString *)value { + self = [super init]; + if (!self) { + return nil; + } + + self.key = key; + self.value = value; + + return self; +} + +- (void)dealloc { + [_key release]; + [_value release]; + [super dealloc]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@=%@", self.key, self.value]; +} + +@end + +#pragma mark - + +extern NSArray * AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding stringEncoding); +extern NSArray * AFQueryStringComponentsFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding stringEncoding); +extern NSArray * AFQueryStringComponentsFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding stringEncoding); + +NSString * AFQueryStringFromParametersWithEncoding(id parameters, NSStringEncoding stringEncoding) { + return [[AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(parameters, nil, stringEncoding) valueForKeyPath:@"description"] componentsJoinedByString:@"&"]; +} + +AFQueryStringComponent * AFQueryStringComponentFromKeyAndValueWithEncoding(id key, id value, NSStringEncoding stringEncoding) { + return [[[AFQueryStringComponent alloc] initWithKey:AFURLEncodedStringFromStringWithEncoding([key description], stringEncoding) value:AFURLEncodedStringFromStringWithEncoding([value description], stringEncoding)] autorelease]; +} + +NSArray * AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey, NSStringEncoding stringEncoding) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; if([parameters isKindOfClass:[NSDictionary class]]) { - [mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters, baseKey)]; + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersDictionaryAtBaseKeyWithEncoding(parameters, baseKey, stringEncoding)]; } else if([parameters isKindOfClass:[NSArray class]]) { - [mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(parameters, baseKey)]; + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersArrayAtBaseKeyWithEncoding(parameters, baseKey, stringEncoding)]; } else { - [mutableParameterComponents addEntriesFromDictionary:AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(parameters, baseKey)]; + [mutableQueryStringComponents addObject:AFQueryStringComponentFromKeyAndValueWithEncoding(baseKey, parameters, stringEncoding)]; } - return mutableParameterComponents; + return mutableQueryStringComponents; } -NSDictionary * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey){ - NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; +NSArray * AFQueryStringComponentsFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding stringEncoding){ + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - id key = nil; - NSEnumerator *enumerator = [parameters keyEnumerator]; - while ((key = [enumerator nextObject])) { + [parameters enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSString *nextKey = baseKey ? [NSString stringWithFormat:@"%@[%@]", baseKey, key] : key; - [mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersAtBaseKeyWithEncoding([parameters valueForKey:key], nextKey)]; - } - - return mutableParameterComponents; + if (nextKey && value) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(value, nextKey, stringEncoding)]; + } + }]; + + return mutableQueryStringComponents; } -NSDictionary * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey) { - NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; +NSArray * AFQueryStringComponentsFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding stringEncoding) { + NSMutableArray *mutableQueryStringComponents = [NSMutableArray array]; - for (id value in parameters) { + [parameters enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) { NSString *nextKey = [NSString stringWithFormat:@"%@[]", baseKey]; - [mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersAtBaseKeyWithEncoding(value, nextKey)]; - } - - return mutableParameterComponents; -} + if (nextKey && value) { + [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(value, nextKey, stringEncoding)]; + } + }]; -NSDictionary * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key) { - return [NSDictionary dictionaryWithObject:parameter forKey:key]; + return mutableQueryStringComponents; } static NSString * AFJSONStringFromParameters(NSDictionary *parameters) { @@ -398,18 +438,10 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil]; __block AFMultipartFormData *formData = [[AFMultipartFormData alloc] initWithStringEncoding:self.stringEncoding]; - [AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters, nil) enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { - NSData *data = nil; - - if ([value isKindOfClass:[NSData class]]) { - data = value; - } else { - data = [[value description] dataUsingEncoding:self.stringEncoding]; - } - - [formData appendPartWithFormData:data name:[key description]]; - }]; - + for (AFQueryStringComponent *component in AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(parameters, nil, self.stringEncoding)) { + [formData appendPartWithFormData:[component.value dataUsingEncoding:self.stringEncoding] name:component.key]; + } + if (block) { block(formData); }