Заманчиво хотеть использовать URLQueryAllowedCharacterSet
, но это не сработает для всех строк.Примечательно, что &
и +
пройдут без спасения.
Если вам интересно, почему мы должны в процентах экранировать &
и +
, то это потому, что эти два символа имеют особое значение в запросах x-www-form-urlencoded
.&
используется для разделения пар ключ-значение в запросе x-www-form-urlencoded
, поэтому он усекает ваш пароль.И большинство веб-сервисов переводят +
в пробел, так что вы также захотите избежать этого.
Итак, давайте сначала определим набор символов, который будет работать:
// NSCharacterSet+URLQueryValueAllowed.h
@interface NSCharacterSet (URLQueryValueAllowed)
@property (class, readonly, copy) NSCharacterSet *URLQueryValueAllowedCharacterSet;
@end
и
// NSCharacterSet+URLQueryValueAllowed.m
@implementation NSCharacterSet (URLQueryValueAllowed)
+ (NSCharacterSet *)URLQueryValueAllowedCharacterSet {
static dispatch_once_t onceToken;
static NSCharacterSet *queryValueAllowed;
dispatch_once(&onceToken, ^{
NSMutableCharacterSet *allowed = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
NSString *generalDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
NSString *subDelimitersToEncode = @"!$&'()*+,;=";
[allowed removeCharactersInString:generalDelimitersToEncode];
[allowed removeCharactersInString:subDelimitersToEncode];
queryValueAllowed = [allowed copy];
});
return queryValueAllowed;
}
@end
Затем, чтобы упростить нам жизнь, давайте определим NSDictionary
категория для процентного кодирования словаря:
// NSDictionary+PercentEncoded.h
@interface NSDictionary (PercentEncoded)
- (NSString *)percentEncodedString;
- (NSData *)percentEncodedData;
@end
и
// NSDictionary+PercentEncoded.m
@implementation NSDictionary (PercentEncoded)
- (NSString *)percentEncodedString {
NSMutableArray<NSString *> *results = [NSMutableArray array];
NSCharacterSet *allowed = [NSCharacterSet URLQueryValueAllowedCharacterSet];
for (NSString *key in self.allKeys) {
NSString *encodedKey = [key stringByAddingPercentEncodingWithAllowedCharacters:allowed];
NSString *value = [[self objectForKey:key] description];
NSString *encodedValue = [value stringByAddingPercentEncodingWithAllowedCharacters:allowed];
[results addObject:[NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue]];
}
return [results componentsJoinedByString:@"&"];
}
- (NSData *)percentEncodedData {
return [[self percentEncodedString] dataUsingEncoding:NSUTF8StringEncoding];
}
@end
Тогда ваш код приложения может сделать:
NSDictionary *dictionary = @{@"login": userName, @"password": userPassword};
NSData *body = [dictionary percentEncodedData];