[Issue #619] Sorting dictionary keys with caseInsensitiveCompare to ensure deterministic ordering of query string parameters, which may otherwise cause ambiguous representations of nested parameters

This commit is contained in:
Mattt Thompson 2012-11-05 12:03:23 -08:00
parent 9cde4e4584
commit 5b32b45469
2 changed files with 10 additions and 3 deletions

View file

@ -113,6 +113,8 @@ typedef enum {
/**
The `AFHTTPClientParameterEncoding` value corresponding to how parameters are encoded into a request body. This is `AFFormURLParameterEncoding` by default.
@warning Some nested parameter structures, such as a keyed array of hashes containing inconsistent keys (i.e. `@{@"": @[@{@"a" : @(1)}, @{@"b" : @(2)}]}`), cannot be unambiguously represented in query strings. It is strongly recommended that an unambiguous encoding, such as `AFJSONParameterEncoding`, is used when posting complicated or nondeterministic parameter structures.
*/
@property (nonatomic, assign) AFHTTPClientParameterEncoding parameterEncoding;
@ -461,7 +463,7 @@ typedef enum {
}
`AFFormURLParameterEncoding`
Parameters are encoded into field/key pairs in the URL query string for `GET` `HEAD` and `DELETE` requests, and in the message body otherwise.
Parameters are encoded into field/key pairs in the URL query string for `GET` `HEAD` and `DELETE` requests, and in the message body otherwise. Dictionary keys are sorted with the `caseInsensitiveCompare:` selector of their description, in order to mitigate the possibility of ambiguous query strings being generated non-deterministically. See the warning for the `parameterEncoding` property for additional information.
`AFJSONParameterEncoding`
Parameters are encoded into JSON in the message body.

View file

@ -146,8 +146,13 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
if([value isKindOfClass:[NSDictionary class]]) {
[value enumerateKeysAndObjectsUsingBlock:^(id nestedKey, id nestedValue, BOOL *stop) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(caseInsensitiveCompare:)];
[[[value allKeys] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] enumerateObjectsUsingBlock:^(id nestedKey, NSUInteger idx, BOOL *stop) {
id nestedValue = [value objectForKey:nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}];
} else if([value isKindOfClass:[NSArray class]]) {
[value enumerateObjectsUsingBlock:^(id nestedValue, NSUInteger idx, BOOL *stop) {