【问题标题】:Objective-C: How to add query parameter to NSURL?Objective-C:如何向 NSURL 添加查询参数?
【发布时间】:2011-09-12 16:11:08
【问题描述】:

假设我有一个NSURL?不管它是否已经有一个空的查询字符串,如何在NSURL 的query 中添加一个或多个参数?也就是说,有人知道这个函数的实现吗?

- (NSURL *)URLByAppendingQueryString:(NSString *)queryString

为了满足这个NSURL+AdditionsSpec.h文件:

#import "NSURL+Additions.h"
#import "Kiwi.h"

SPEC_BEGIN(NSURL_AdditionsSpec)

describe(@"NSURL+Additions", ^{
    __block NSURL *aURL;

    beforeEach(^{
        aURL = [[NSURL alloc] initWithString:@"http://www.example.com"];
        aURLWithQuery = [[NSURL alloc] initWithString:@"http://www.example.com?key=value"];
    });

    afterEach(^{
        [aURL release];
        [aURLWithQuery release];
    });

    describe(@"-URLByAppendingQueryString:", ^{
        it(@"adds to plain URL", ^{
            [[[[aURL URLByAppendingQueryString:@"key=value&key2=value2"] query] should]
             equal:@"key=value&key2=value2"];
        });

        it(@"appends to the existing query sting", ^{
            [[[[aURLWithQuery URLByAppendingQueryString:@"key2=value2&key3=value3"] query] should]
             equal:@"key=value&key2=value2&key3=value3"];
        });
    });
});

SPEC_END

【问题讨论】:

    标签: objective-c query-string nsurl


    【解决方案1】:

    从 iOS 7 开始,您可以使用非常简单易用的 NSURLComponents。看看这些例子:

    示例 1

    NSString *urlString = @"https://mail.google.com/mail/u/0/?shva=1#inbox";
    NSURLComponents *components = [[NSURLComponents alloc] initWithString:urlString];
    
    NSLog(@"%@ - %@ - %@ - %@", components.scheme, components.host, components.query, components.fragment);
    

    示例 2

    NSString *urlString = @"https://mail.google.com/mail/u/0/?shva=1#inbox";
    NSURLComponents *components = [[NSURLComponents alloc] initWithString:urlString];
    
    if (components) {
        //good URL
    } else {
        //bad URL
    }
    

    示例 3

    NSURLComponents *components = [NSURLComponents new];
    [components setScheme:@"https"];
    [components setHost:@"mail.google.com"];
    [components setQuery:@"shva=1"];
    [components setFragment:@"inbox"];
    [components setPath:@"/mail/u/0/"];
    
    [self.webview loadRequest:[[NSURLRequest alloc] initWithURL:[components URL]]];
    

    但是您可以使用 NSURLComponents 做许多其他事情,请查看 Apple 文档或此链接上的 NSURLComponents 类参考:http://nshipster.com/nsurl/

    【讨论】:

    • 太棒了!正是我想要的。
    【解决方案2】:

    这是一个通过你的规范的实现:

    @implementation NSURL (Additions)
    
    - (NSURL *)URLByAppendingQueryString:(NSString *)queryString {
        if (![queryString length]) {
            return self;
        }
    
        NSString *URLString = [[NSString alloc] initWithFormat:@"%@%@%@", [self absoluteString],
                               [self query] ? @"&" : @"?", queryString];
        NSURL *theURL = [NSURL URLWithString:URLString];
        [URLString release];
        return theURL;
    }
    
    @end
    

    这是NSString的一个实现:

    @implementation NSString (Additions)
    
    - (NSURL *)URLByAppendingQueryString:(NSString *)queryString {
        if (![queryString length]) {
            return [NSURL URLWithString:self];
        }
    
        NSString *URLString = [[NSString alloc] initWithFormat:@"%@%@%@", self,
                               [self rangeOfString:@"?"].length > 0 ? @"&" : @"?", queryString];
        NSURL *theURL = [NSURL URLWithString:URLString];
        [URLString release];
        return theURL;
    }
    
    // Or:
    
    - (NSString *)URLStringByAppendingQueryString:(NSString *)queryString {
        if (![queryString length]) {
            return self;
        }
        return [NSString stringWithFormat:@"%@%@%@", self,
                [self rangeOfString:@"?"].length > 0 ? @"&" : @"?", queryString];
    }
    
    @end
    

    【讨论】:

    • 很好的答案,但为了完全安全,您应该在构建 URLString 时使用 [queryString stringByAddingPercentEscapesUsingEncoding:] ,否则在 queryString 还不是“url 友好”的情况下,结果 URL 将得到 nil(例如,当参数值中有空格时)。
    • 不适用于带有井号 (#) 的网址,其中新的查询字符串参数应附加在井号之前。
    • 使用ARC时[URLString release];不需要。
    • 您现在应该真正使用NSURLComponents,正如下面 Salmo 的回答中所建议的那样。
    • 我觉得这不是一个很好的解决方案,RFC在URL上定义了一个复杂的格式,例如这个解决方案会破坏源URL的fragment组件。
    【解决方案3】:

    iOS8+ 现代方式

    向 min60.com 上的 url 添加(或替换 'ref' 值(如果存在))ref=impm

    if ([[url host] hasSuffix:@"min60.com"]) {
    
        NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO];
        NSURLQueryItem * newQueryItem = [[NSURLQueryItem alloc] initWithName:@"ref" value:@"impm"];
        NSMutableArray * newQueryItems = [NSMutableArray arrayWithCapacity:[components.queryItems count] + 1];
        for (NSURLQueryItem * qi in components.queryItems) {
            if (![qi.name isEqual:newQueryItem.name]) {
                [newQueryItems addObject:qi];
            }
        }
        [newQueryItems addObject:newQueryItem];
        [components setQueryItems:newQueryItems];
    
        url = [components URL];
    }
    

    【讨论】:

      【解决方案4】:

      对于那些不想在使用NSURLComponents 构建NSURL 时编写样板代码的人来说只是一个友好的帖子。
      从 iOS8 开始,我们有 NSURLQueryItem 帮助快速构建 URL 请求。

      我写了一个方便的类别来简化工作,你可以在这里获取:URLQueryBuilder
      以下是使用它的简单示例:

      NSString *baseURL = @"https://google.com/search";
      NSDictionary *items = @{
          @"q"  : @"arsenkin.com",
          @"hl" : @"en_US",
          @"lr" : @"lang_en"
      };
      
      NSURL *URL = [NSURL ars_queryWithString:baseURL queryElements:items];  
      // https://google.com/search?q=arsenkin.com&hl=en_US&lr=lang_en
      

      【讨论】:

      • 感谢类别贡献。
      【解决方案5】:

      我有一个NSURLComponents 的扩展,它可以快速添加查询项:

      extension NSURLComponents {
      
          func appendQueryItem(name name: String, value: String) {
              var queryItems: [NSURLQueryItem] = self.queryItems ?? [NSURLQueryItem]()
              queryItems.append(NSURLQueryItem(name: name, value: value))
              self.queryItems = queryItems
          }
      
      }
      

      使用,

      let components = NSURLComponents(string: urlString)!
      components.appendQueryItem(name: "key", value: "value")
      

      【讨论】:

      • 这个可能有效,但需要添加扩展。
      【解决方案6】:

      如果您使用的是 RestKit,它会提供 additions to NSString。其中之一是:

      - (NSString *)stringByAppendingQueryParameters:(NSDictionary *)queryParameters
      

      所以你可以这样做:

      NSDictionary *shopParams = [NSDictionary dictionaryWithKeysAndObjects:
                                      @"limit",@"20", 
                                      @"location",@"latitude,longitude",
                                      nil];
      NSString *pathWithQuery = [@"/api/v1/shops.json" stringByAppendingQueryParameters:shopParams]
      

      【讨论】:

      • RKAdditions 类别现在不再可用。
      【解决方案7】:

      正如其他人所提到的,您可以使用NSURLComponents 来构造 URL。

      @implementation NSURL (Additions)
      
      - (NSURL *)URLByAppendingQueryParameters:(NSDictionary *)queryParameters
      {        
          NSURLComponents *components = [[NSURLComponents alloc] initWithURL:self resolvingAgainstBaseURL:NO];
          NSMutableArray *queryItems = [NSMutableArray array:components.queryItems];
          for (NSString *key in [queryParameters allKeys]) {
              NSURLQueryItem *queryItem = [[NSURLQueryItem alloc] initWithName:key value:queryParameters[key]];
              [queryItems addObject:queryItem];
          }
          components.queryItems = queryItems;
          return [components URL];
      }
      
      @end
      

      【讨论】:

        【解决方案8】:

        NSURL 是不可变的,所以你不能直接基于 NSURL 实现这个功能。相反,您必须获取 URL 的字符串表示形式,将参数附加到该字符串,然后创建一个新的 NSURL。

        这听起来不是一个好的解决方案。除非有充分的理由,否则最好在最后一刻使用字符串,并且仅在您有完整的请求时才创建一个 NSURL。

        【讨论】:

          猜你喜欢
          • 2019-03-24
          • 1970-01-01
          • 1970-01-01
          • 2012-08-06
          • 1970-01-01
          • 2012-11-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-13
          相关资源
          最近更新 更多