[Issue #243] Fixing AFQueryStringFromParametersWithEncoding to encode nested array values correctly

This commit is contained in:
Mattt Thompson 2012-03-19 12:44:06 -04:00
parent 34b7d01af8
commit acd6e49294

View file

@ -96,7 +96,7 @@ static NSString * AFBase64EncodedStringFromString(NSString *string) {
} }
NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding) { 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. 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]; return [(NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(encoding)) autorelease];
} }
extern NSDictionary * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey); #pragma mark -
extern NSDictionary * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey);
extern NSDictionary * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey);
extern NSDictionary * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key);
NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding) { @interface AFQueryStringComponent : NSObject {
NSMutableArray *queryStringArray = [NSMutableArray array]; @private
NSString *_key;
[AFQueryParametersFromParametersAtBaseKeyWithEncoding(parameters, nil) enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { NSString *_value;
[queryStringArray addObject:[NSString stringWithFormat:@"%@=%@", AFURLEncodedStringFromStringWithEncoding([key description], encoding), AFURLEncodedStringFromStringWithEncoding([obj description], encoding)]];
}];
return [queryStringArray componentsJoinedByString:@"&"];
} }
NSDictionary * AFQueryParametersFromParametersAtBaseKeyWithEncoding(id parameters, NSString *baseKey) { @property (readwrite, nonatomic, retain) id key;
NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; @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]]) { if([parameters isKindOfClass:[NSDictionary class]]) {
[mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters, baseKey)]; [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersDictionaryAtBaseKeyWithEncoding(parameters, baseKey, stringEncoding)];
} else if([parameters isKindOfClass:[NSArray class]]) { } else if([parameters isKindOfClass:[NSArray class]]) {
[mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(parameters, baseKey)]; [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersArrayAtBaseKeyWithEncoding(parameters, baseKey, stringEncoding)];
} else { } else {
[mutableParameterComponents addEntriesFromDictionary:AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(parameters, baseKey)]; [mutableQueryStringComponents addObject:AFQueryStringComponentFromKeyAndValueWithEncoding(baseKey, parameters, stringEncoding)];
} }
return mutableParameterComponents; return mutableQueryStringComponents;
} }
NSDictionary * AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey){ NSArray * AFQueryStringComponentsFromParametersDictionaryAtBaseKeyWithEncoding(NSDictionary *parameters, NSString *baseKey, NSStringEncoding stringEncoding){
NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
id key = nil; [parameters enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
NSEnumerator *enumerator = [parameters keyEnumerator];
while ((key = [enumerator nextObject])) {
NSString *nextKey = baseKey ? [NSString stringWithFormat:@"%@[%@]", baseKey, key] : key; NSString *nextKey = baseKey ? [NSString stringWithFormat:@"%@[%@]", baseKey, key] : key;
[mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersAtBaseKeyWithEncoding([parameters valueForKey:key], nextKey)]; if (nextKey && value) {
} [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(value, nextKey, stringEncoding)];
}
}];
return mutableParameterComponents; return mutableQueryStringComponents;
} }
NSDictionary * AFQueryParametersFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey) { NSArray * AFQueryStringComponentsFromParametersArrayAtBaseKeyWithEncoding(NSArray *parameters, NSString *baseKey, NSStringEncoding stringEncoding) {
NSMutableDictionary *mutableParameterComponents = [NSMutableDictionary dictionary]; NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
for (id value in parameters) { [parameters enumerateObjectsUsingBlock:^(id value, NSUInteger idx, BOOL *stop) {
NSString *nextKey = [NSString stringWithFormat:@"%@[]", baseKey]; NSString *nextKey = [NSString stringWithFormat:@"%@[]", baseKey];
[mutableParameterComponents addEntriesFromDictionary:AFQueryParametersFromParametersAtBaseKeyWithEncoding(value, nextKey)]; if (nextKey && value) {
} [mutableQueryStringComponents addObjectsFromArray:AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(value, nextKey, stringEncoding)];
}
}];
return mutableParameterComponents; return mutableQueryStringComponents;
}
NSDictionary * AFQueryStringComponentFromParameterAtBaseKeyWithEncoding(id parameter, NSString *key) {
return [NSDictionary dictionaryWithObject:parameter forKey:key];
} }
static NSString * AFJSONStringFromParameters(NSDictionary *parameters) { static NSString * AFJSONStringFromParameters(NSDictionary *parameters) {
@ -398,17 +438,9 @@ static void AFReachabilityCallback(SCNetworkReachabilityRef __unused target, SCN
NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil]; NSMutableURLRequest *request = [self requestWithMethod:method path:path parameters:nil];
__block AFMultipartFormData *formData = [[AFMultipartFormData alloc] initWithStringEncoding:self.stringEncoding]; __block AFMultipartFormData *formData = [[AFMultipartFormData alloc] initWithStringEncoding:self.stringEncoding];
[AFQueryParametersFromParametersDictionaryAtBaseKeyWithEncoding(parameters, nil) enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { for (AFQueryStringComponent *component in AFQueryStringComponentsFromParametersAtBaseKeyWithEncoding(parameters, nil, self.stringEncoding)) {
NSData *data = nil; [formData appendPartWithFormData:[component.value dataUsingEncoding:self.stringEncoding] name:component.key];
}
if ([value isKindOfClass:[NSData class]]) {
data = value;
} else {
data = [[value description] dataUsingEncoding:self.stringEncoding];
}
[formData appendPartWithFormData:data name:[key description]];
}];
if (block) { if (block) {
block(formData); block(formData);