当参数计数可变时,使用NSString
构建查询的最明智的方法是什么?
假设我有一个Web服务,它返回按参数列表过滤的国家/地区列表。例如:
NSString *continent = @"europe";
NSString *language = @"german";
NSString *timeZone = @"UTC+1";
// to be continued
因此我的查询字符串将是:
[NSString stringWithFormat:@"/getCountries?continent=%@&language=%@&timezone=%@",
continent, language, timeZone];
我的回复将包含
奥地利,德国和瑞士
因为它们匹配所有三个参数。
现在我正在寻找最聪明的方法(例如,不是if
s的意大利面条代码)来构建此查询,当0:n这些参数可能为零时。 (例如,*timeZone
为nil
时,网址不应包含timezone=nil
)结果应为NSString
。
答案 0 :(得分:5)
我会创造一个做这件事的对象并且做得对。
您必须注意的一件事是确保正确地逃避所有名称和值。例如,如果你传入一个“fred& wilma”的值,那就不应该在URL中显示为“members = fred& wilma” - 你需要&被转义(%26
)。
您可以在装订时简单地转义每个字符串,但忘记这样做太容易了。代码中重复的任何内容都很容易错过。
这是我为此制作对象的主要原因。该对象不仅会将字符串拼接在一起,还会适当地逃避它们。
我假设你以后不需要查找键的值。您只想构建一个URL。
首先,对象应该私有地保存NSMutableString(不是公共@property
- 私有@property
或实例变量)。它应该在初始化时创建它,并在其整个生命中保持它。
对象的指定初始值设定项应该类似于initWithResourceURLString:
。此方法向字符串发送mutableCopy
消息,并将结果分配给实例变量或私有属性。 init
应抛出异常(最容易通过断言false
)。
对象还应该有一个unichar
的实例变量。 initWithResourceURLString:
应将其设置为'?'
。
对象应响应setQueryParameterWithName:toValue:
之类的消息。每个参数都应该是一个字符串。如果任一字符串为nil
,则该方法只返回。
该方法发送每个字符串a stringByAddingPercentEscapesUsingEncoding:
message,然后使用以下格式发送可变字符串appendFormat:
消息:
%C%@=%@
参数是unichar
实例变量中的字符,转义参数名称和转义值。然后它将字符变量设置为&
。
对象应该通过返回可变字符串的副本(如果您正在使用MRC自动释放)来响应诸如constructedURLString
之类的消息。
然后您将使用该对象:
builder = [[MyURLBuilder alloc] initWithResourceURLString:@"http://example.com/getCountries"]; //Add autorelease under MRC
[builder setQueryParameterName:@"continent" toValue:continent];
[builder setQueryParameterName:@"language" toValue:language];
[builder setQueryParameterName:@"timeZone" toValue:timeZone];
NSURL *finishedURL = [NSURL URLWithString:[builder constructedURLString]];
优点:
nil
。所有其他代码都不应该担心它。nil
的空字符串替换任何或仅一些参数的支持。您对该课程所做的任何更改都将立即用于您可能拥有的任何其他URL构建作业。答案 1 :(得分:2)
您可以使用三元运算符,然后:
[NSString stringWithFormat:@"/getCountries?continent=%@&language=%@&timezone=%@", (continent ? continent : @""), (language ? language : @""), (timezone ? timezone : @"")];
答案 2 :(得分:2)
将参数存储在NSDictionary中而不是单独的NSStrings中。然后走字典来构建你的param字符串。此实现将处理0到N个参数。您也可以通过参数化基本URL来概括它以在API中使用。
NSMutableString *paramString = [NSMutableString stringWithString:@"/getCountries"];
NSUInteger paramCount = 0;
[params enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop){
[paramString appendString:(paramCount == 0) ? @"?" : @"&"];
NSString *newParam = [[NSString stringWithFormat:@"%@=%@", key, [params objectForKey:key]] stringByAddingPercentEscapesUsingEncoding:NSStringUTF8Encoding];
[paramString appendString:newParam];
paramCount++;
}];
答案 3 :(得分:1)
@XJones有一个良好的开端,但我认为可以更简单地完成:
NSDictionary *params = ...;
NSMutableArray *pairs = [NSMutableArray array];
for (NSString *key in params) {
NSString *value = [params objectForKey:key];
NSString *pair = [NSString stringWithFormat:@"%@=%@", key, value];
[pairs addObject:pair];
}
NSString *queryString = [pairs componentsJoinedByString:@"&"];
NSString *path = [NSString stringWithFormat:@"/getCountries?%@", queryString];
当然,您需要正确对key
和value
进行网址编码。
答案 4 :(得分:1)
只需将参数放入NSDictionary中,然后根据以下内容构建URL:
NSMutableDictionary * dict = [NSMutableDictionary dictionary];
if (continent) [dict setObject:continent forKey:@"continent"];
if (timeZone) [dict setObject:timeZone forKey:@"timeZone"];
NSMutableString * queryString = [NSMutableString stringWithString:@"/getCountries?"];
NSArray * allKeys = [dict allKeys];
for (NSUInteger i = 0; i < [allKeys count]; i++) {
NSString * key = [allKeys objectAtIndex:i];
NSString * value = [dict objectForKey:key];
NSString * escaped = [value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
[queryString appendFormat:@"%@=%@", key, escaped];
if (i + 1 < [allKeys count]) {
[queryString appendString:@"&"];
}
}