[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) {
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);
}