用于查询字符串的stringWithFormat变量参数列表
当参数数量可变时,用NSString
建立查询的最智能方法是什么?用于查询字符串的stringWithFormat变量参数列表
比方说,我有一个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
时,该URL不应包含timezone=nil
)结果应该是NSString
。在一个NSDictionary
[NSString stringWithFormat:@"/getCountries?continent=%@&language=%@&timezone=%@", (continent ? continent : @""), (language ? language : @""), (timezone ? timezone : @"")];
我会做一个对象,做这样一两件事,并且做得非常正确。
您必须注意的一个关键是确保您正确地转义所有名称和值。例如,如果您传递的值为“fred & wilma”,那么该URL中不应显示为“members = fred & wilma” - 您需要将&转义(如%26
)。
当你装订时,你可以简单地逃避每个字符串,但是忘记这么做会很容易。代码中重复的任何内容都很容易被忽略。
这是我为此制作对象的主要原因。这个对象不仅会将线串起来,而且还会适当地逃避它们。
我假设你以后不需要查找键值。你只是想构建一个URL。
首先,对象应该私下拥有一个NSMutableString(不是公开的@property
--私有@property
或实例变量)。它应该在初始化时创建并在整个生命中保持它。
该对象的指定初始化程序应该是类似于initWithResourceURLString:
。此方法发送字符串a mutableCopy
消息并将结果分配给实例变量或私有属性。 init
应该抛出异常(通过声明false
最容易)。
该对象还应该有一个实例变量为单个unichar
。 initWithResourceURLString:
应该将其设置为'?'
。
对象应该响应一条消息,如setQueryParameterWithName:toValue:
。每个参数应该是一个字符串。如果任一字符串是nil
,则该方法仅返回。
也就是说方法发送的每个字符串a stringByAddingPercentEscapesUsingEncoding:
message,然后将可变的字符串的appendFormat:
消息的格式如下:
%C%@=%@
与参数是在unichar
实例变量,转义参数名的字符,和逃脱的价值。然后它将字符变量设置为&
。
该对象应通过返回可变字符串的副本(如果使用MRC,则为autoreleased)来响应诸如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
正确处理(忽略)。所有其他代码不需要担心。 - 订单被保留。字典可能会改变参数的顺序。这应该不重要(服务器不应该在意),但如果它确实如此,那么您需要一个保留订单的解决方案。
- 为这个对象编写单元测试会很容易。
- 如果需要,您可以在多个方向上修改此对象。例如,您可以添加对重置构建的查询URL的支持,而无需修改或分别记住基本URL,从而使您可以将此对象用于多个查询。您还可以添加支持,以便将
nil
的空字符串替换为任何或仅用于某些参数。您对该课程所做的任何更改都会立即提供给您可能拥有的任何其他URL构建作业。
很好的答案。照常! –
谢谢你的详尽答案,我很高兴接受。没有其他办法了。 – Chris
您可以使用三元运算符,然后。然后走字典来建立你的参数字符串。这个实现将处理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++;
}];
右,三元将为空参数做。我希望有一些更优雅的东西。 – Chris
商店PARAMS不是个别NSString的的:
@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];
当然,你要正确的URL编码key
和value
。
只需要把你的参数到一个NSDictionary,并建立基于这样的网址:
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:@"&"];
}
}
在零大陆的情况下,例如,什么是你最好:从查询中删除它或把它,但空的,就像这样:'大陆=语言...' –
@LuizCarlosQuerinoFilho本来我正在寻找一种方法来完全删除零部分,但您的建议也应该在这种情况下工作。 – Chris