stringWithFormat中的变量参数列表用于查询字符串

时间:2011-11-24 22:19:44

标签: objective-c cocoa nsstring

当参数计数可变时,使用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这些参数可能为零时。 (例如,*timeZonenil时,网址不应包含timezone=nil)结果应为NSString

5 个答案:

答案 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。所有其他代码都不应该担心它。
  • 保留订单。字典可以改变参数的顺序。这应该不重要(服务器不应该关心),但如果确实如此,那么您需要一个保留订单的解决方案。
  • 为这个对象编写单元测试很容易。
  • 如果需要,可以在多个方向修改此对象。例如,您可以添加对重置构造的查询URL的支持,而无需修改或不必单独记住基本URL,从而使您可以将此对象重用于多个查询。您还可以添加对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];

当然,您需要正确对keyvalue进行网址编码。

答案 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:@"&"];
    }
}