所有URL处理对象都位于标准Cocoa库(NSURL,NSMutableURL,NSMutableURLRequest等)中,我知道我必须忽略一种以编程方式编写GET请求的简单方法。
目前我手动追加“?”其次是由“&”连接的名称值对,但我的所有名称和值对都需要手动编码,因此NSMutableURLRequest在尝试连接到URL时不会完全失败。
这感觉就像我应该能够使用预先烘焙的API ......是否有任何开箱即可将查询参数的NSDictionary附加到NSURL?还有其他方法可以解决这个问题吗?
答案 0 :(得分:132)
在iOS8和OS X 10.10中引入NSURLQueryItem
,可用于构建查询。来自NSURLQueryItem上的文档:
NSURLQueryItem对象表示URL查询部分中项目的单个名称/值对。您将查询项与NSURLComponents对象的queryItems属性一起使用。
要创建一个,请使用指定的初始值设定项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;
我还写了blog post关于如何使用NSURLComponents
和NSURLQueryItems
构建网址的问题。
答案 1 :(得分:47)
您可以为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更详细地讨论了它。
答案 2 :(得分:29)
我想使用Chris的答案,但它不是为自动引用计数(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;
}
答案 3 :(得分:22)
如果值是字符串,则其他答案很有效,但是如果值是字典或数组,则此代码将处理该字符。
重要的是要注意,没有通过查询字符串传递数组/字典的标准方法,但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
}
<强>富=栏&安培;翻译[酮] = UNO&安培;翻译[2] =&DOS放大器;翻译[3] = TRES 强>
[foo] => bar
[translations] =>
{
uno
dos
tres
}
<强>富=栏&安培;翻译[] = UNO&安培;翻译[] = DOS&安培;翻译[] = TRES 强>
答案 4 :(得分:8)
我重构并转换为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);
}
答案 5 :(得分:5)
如果您已经在使用AFNetworking(就像我一样),您可以使用它的课程AFHTTPRequestSerializer
来创建所需的NSURLRequest
。
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"YOUR_URL" parameters:@{PARAMS} error:nil];
如果您只需要工作的网址,请使用NSURLRequest.URL
。
答案 6 :(得分:3)
以下是 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
}
答案 7 :(得分:3)
我采用了Joel的建议,使用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"])
答案 8 :(得分:2)
我有另一个解决方案:
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 :(得分:2)
-(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 :(得分:1)
另一种解决方案是,如果您使用RestKit,RKURLEncodedSerialization
中的一个名为RKURLEncodedStringFromDictionaryWithEncoding
的函数就会完全符合您的要求。
答案 11 :(得分:1)
在Objective-c
中将NSDictionary转换为url查询字符串的简单方法Ex:first_name = Steve&amp; middle_name = Gates&amp; last_name = Jobs&amp; 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 :(得分:0)
如果你已经在使用 AFNetwork,你可以使用他们内置的序列化器来生成一个编码的 URL;
NSString *baseURL = @"https://api.app.com/parse";
NSDictionary *mutableParameters = [[NSMutableDictionary alloc] initWithObjectsAndKeys:@"true",@"option1", data, @"option2", token, @"token", @"3.0", @"app", nil];
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:baseURL parameters:mutableParameters error:nil];
NSString *urlPath = request.URL.absoluteString;
NSLog(@"%@", urlPath); // https://api.app.com/parse?option1=true&option2=datavalue&token=200%3ATEST%3AENCODE ....
注意;这是上述答案的扩展。编辑队列已满,因此无法添加到现有答案中。