Создание параметров URL-запроса из объектов NSDictionary в ObjectiveC
Со всеми объектами обработки URL-адресов, расположенными в стандартных библиотеках Cocoa (NSURL, NSMutableURL, NSMutableURLRequest и т.д.), я знаю, что я должен игнорировать простой способ программно составить запрос GET.
В настоящее время я вручную добавляю "?" за которыми следуют пары значений имени, присоединенных "&", но все мои пары имени и значения должны быть закодированы вручную, поэтому NSMutableURLRequest не терпит неудачу, когда он пытается подключиться к URL.
Это похоже на то, что я должен использовать предварительно обработанный API для... есть ли что-нибудь из коробки, чтобы добавить NSDictionary параметров запроса в NSURL? Есть ли другой способ, которым я должен подходить к этому?
Ответы
Ответ 1
Введенные в iOS8 и OS X 10.10 NSURLQueryItem
, которые могут использоваться для создания запросов. В документах NSURLQueryItem:
Объект NSURLQueryItem представляет собой единую пару имя/значение для элемента в части запроса URL-адреса. Вы используете элементы запроса с свойством queryItems объекта NSURLComponents.
Чтобы создать один, используйте назначенный инициализатор queryItemWithName:value:
, а затем добавьте их в NSURLComponents
, чтобы сгенерировать NSURL
. Например:
NSURLComponents *components = [NSURLComponents componentsWithString:@"http://stackoverflow.com"];
NSURLQueryItem *search = [NSURLQueryItem queryItemWithName:@"q" value:@"ios"];
NSURLQueryItem *count = [NSURLQueryItem queryItemWithName:@"count" value:@"10"];
components.queryItems = @[ search, count ];
NSURL *url = components.URL; // http://stackoverflow.com?q=ios&count=10
Обратите внимание, что знак вопроса и амперсанд автоматически обрабатываются. Создание NSURL
из словаря параметров так же просто, как:
NSDictionary *queryDictionary = @{ @"q": @"ios", @"count": @"10" };
NSMutableArray *queryItems = [NSMutableArray array];
for (NSString *key in queryDictionary) {
[queryItems addObject:[NSURLQueryItem queryItemWithName:key value:queryDictionary[key]]];
}
components.queryItems = queryItems;
Я также написал сообщение в блоге о том, как создавать URL-адреса с NSURLComponents
и NSURLQueryItems
.
Ответ 2
Вы можете создать категорию для NSDictionary
, чтобы сделать это - стандартного способа в библиотеке Cocoa я не смог найти. Код, который я использую, выглядит следующим образом:
// file "NSDictionary+UrlEncoding.h"
#import <cocoa/cocoa.h>
@interface NSDictionary (UrlEncoding)
-(NSString*) urlEncodedString;
@end
с этой реализацией:
// file "NSDictionary+UrlEncoding.m"
#import "NSDictionary+UrlEncoding.h"
// helper function: get the string form of any object
static NSString *toString(id object) {
return [NSString stringWithFormat: @"%@", object];
}
// helper function: get the url encoded string form of any object
static NSString *urlEncode(id object) {
NSString *string = toString(object);
return [string stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}
@implementation NSDictionary (UrlEncoding)
-(NSString*) urlEncodedString {
NSMutableArray *parts = [NSMutableArray array];
for (id key in self) {
id value = [self objectForKey: key];
NSString *part = [NSString stringWithFormat: @"%@=%@", urlEncode(key), urlEncode(value)];
[parts addObject: part];
}
return [parts componentsJoinedByString: @"&"];
}
@end
Я думаю, что код довольно прост, но я более подробно обсужу его на http://blog.ablepear.com/2008/12/urlencoding-category-for-nsdictionary.html.
Ответ 3
Я хотел использовать ответ Криса, но он не был написан для Automatic Reference Counting (ARC), поэтому я его обновил. Я думал, что приложу свое решение, если у кого-то еще будет такая же проблема. Примечание. замените self
именем экземпляра или класса, где это необходимо.
+(NSString*)urlEscapeString:(NSString *)unencodedString
{
CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString;
NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ", kCFStringEncodingUTF8);
CFRelease(originalStringRef);
return s;
}
+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary
{
NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString];
for (id key in dictionary) {
NSString *keyString = [key description];
NSString *valueString = [[dictionary objectForKey:key] description];
if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) {
[urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
} else {
[urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]];
}
}
return urlWithQuerystring;
}
Ответ 4
Другие ответы отлично работают, если значения являются строками, однако, если значения являются словарями или массивами, этот код будет обрабатывать это.
Важно отметить, что нет стандартного способа передачи массива/словаря по строке запроса, но PHP обрабатывает этот вывод просто отлично
-(NSString *)serializeParams:(NSDictionary *)params {
/*
Convert an NSDictionary to a query string
*/
NSMutableArray* pairs = [NSMutableArray array];
for (NSString* key in [params keyEnumerator]) {
id value = [params objectForKey:key];
if ([value isKindOfClass:[NSDictionary class]]) {
for (NSString *subKey in value) {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)[value objectForKey:subKey],
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, escaped_value]];
}
} else if ([value isKindOfClass:[NSArray class]]) {
for (NSString *subValue in value) {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)subValue,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, escaped_value]];
}
} else {
NSString* escaped_value = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)[params objectForKey:key],
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8);
[pairs addObject:[NSString stringWithFormat:@"%@=%@", key, escaped_value]];
[escaped_value release];
}
}
return [pairs componentsJoinedByString:@"&"];
}
<сильные > Примеры
[foo] => bar
[translations] =>
{
[one] => uno
[two] => dos
[three] => tres
}
Foo = бар & переводы [один] = UNO & переводы [два] = дос & переводы [три] = Tres
[foo] => bar
[translations] =>
{
uno
dos
tres
}
Foo = бар & переводы [] = UNO & переводы [] = дос & переводы [] = Tres
Ответ 5
Я реорганизовал и преобразовал в ARC-ответ AlBeebe
- (NSString *)serializeParams:(NSDictionary *)params {
NSMutableArray *pairs = NSMutableArray.array;
for (NSString *key in params.keyEnumerator) {
id value = params[key];
if ([value isKindOfClass:[NSDictionary class]])
for (NSString *subKey in value)
[pairs addObject:[NSString stringWithFormat:@"%@[%@]=%@", key, subKey, [self escapeValueForURLParameter:[value objectForKey:subKey]]]];
else if ([value isKindOfClass:[NSArray class]])
for (NSString *subValue in value)
[pairs addObject:[NSString stringWithFormat:@"%@[]=%@", key, [self escapeValueForURLParameter:subValue]]];
else
[pairs addObject:[NSString stringWithFormat:@"%@=%@", key, [self escapeValueForURLParameter:value]]];
}
return [pairs componentsJoinedByString:@"&"];
}
- (NSString *)escapeValueForURLParameter:(NSString *)valueToEscape {
return (__bridge_transfer NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (__bridge CFStringRef) valueToEscape,
NULL, (CFStringRef) @"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8);
}
Ответ 6
Если вы уже используете AFNetworking (как это было в случае со мной), вы можете использовать его class AFHTTPRequestSerializer
для создания требуется NSURLRequest
.
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"YOUR_URL" parameters:@{PARAMS} error:nil];
Если вам нужен только URL-адрес вашей работы, используйте NSURLRequest.URL
.
Ответ 7
Вот простой пример в Swift (iOS8 +):
private let kSNStockInfoFetchRequestPath: String = "http://dev.markitondemand.com/Api/v2/Quote/json"
private func SNStockInfoFetchRequestURL(symbol:String) -> NSURL? {
if let components = NSURLComponents(string:kSNStockInfoFetchRequestPath) {
components.queryItems = [NSURLQueryItem(name:"symbol", value:symbol)]
return components.URL
}
return nil
}
Ответ 8
У меня есть другое решение:
http://splinter.com.au/build-a-url-query-string-in-obj-c-from-a-dict
+(NSString*)urlEscape:(NSString *)unencodedString {
NSString *s = (NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
kCFStringEncodingUTF8);
return [s autorelease]; // Due to the 'create rule' we own the above and must autorelease it
}
// Put a query string onto the end of a url
+(NSString*)addQueryStringToUrl:(NSString *)url params:(NSDictionary *)params {
NSMutableString *urlWithQuerystring = [[[NSMutableString alloc] initWithString:url] autorelease];
// Convert the params into a query string
if (params) {
for(id key in params) {
NSString *sKey = [key description];
NSString *sVal = [[params objectForKey:key] description];
// Do we need to add ?k=v or &k=v ?
if ([urlWithQuerystring rangeOfString:@"?"].location==NSNotFound) {
[urlWithQuerystring appendFormat:@"?%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]];
} else {
[urlWithQuerystring appendFormat:@"&%@=%@", [Http urlEscape:sKey], [Http urlEscape:sVal]];
}
}
}
return urlWithQuerystring;
}
Затем вы можете использовать его так:
NSDictionary *params = @{@"username":@"jim", @"password":@"abc123"};
NSString *urlWithQuerystring = [self addQueryStringToUrl:@"https://myapp.com/login" params:params];
Ответ 9
-(NSString*)encodeDictionary:(NSDictionary*)dictionary{
NSMutableString *bodyData = [[NSMutableString alloc]init];
int i = 0;
for (NSString *key in dictionary.allKeys) {
i++;
[bodyData appendFormat:@"%@=",key];
NSString *value = [dictionary valueForKey:key];
NSString *newString = [value stringByReplacingOccurrencesOfString:@" " withString:@"+"];
[bodyData appendString:newString];
if (i < dictionary.allKeys.count) {
[bodyData appendString:@"&"];
}
}
return bodyData;
}
Ответ 10
Я взял рекомендацию Джоэла использовать URLQueryItems
и превратился в Swift Extension (Swift 3)
extension URL
{
/// Creates an NSURL with url-encoded parameters.
init?(string : String, parameters : [String : String])
{
guard var components = URLComponents(string: string) else { return nil }
components.queryItems = parameters.map { return URLQueryItem(name: $0, value: $1) }
guard let url = components.url else { return nil }
// Kinda redundant, but we need to call init.
self.init(string: url.absoluteString)
}
}
(Метод self.init
является своего рода дрянным, но не было NSURL
init с компонентами)
Может использоваться как
URL(string: "http://www.google.com/", parameters: ["q" : "search me"])
Ответ 11
Простой способ преобразования NSDictionary в строку запроса url в Objective-c
Ex: first_name = Steve & middle_name = Gates & last_name = Jobs & address = Palo Alto, California
NSDictionary *sampleDictionary = @{@"first_name" : @"Steve",
@"middle_name" : @"Gates",
@"last_name" : @"Jobs",
@"address" : @"Palo Alto, California"};
NSMutableString *resultString = [NSMutableString string];
for (NSString* key in [sampleDictionary allKeys]){
if ([resultString length]>0)
[resultString appendString:@"&"];
[resultString appendFormat:@"%@=%@", key, [sampleDictionary objectForKey:key]];
}
NSLog(@"QueryString: %@", resultString);
Надежда поможет:)
Ответ 12
Еще одно решение, если вы используете RestKit, существует функция в RKURLEncodedSerialization
, называемая RKURLEncodedStringFromDictionaryWithEncoding
, которая делает именно то, что вы хотите.