如何使用NSURLComponents将+编码到%2B中

时间:2015-07-23 02:27:09

标签: swift encoding nsurl url-encoding

我使用的是NSURLComponents,我似乎无法正确编码查询值。我需要最终网址将+表示为%2B

let baseUrl = NSURL(string: "http://www.example.com")    
let components = NSURLComponents(URL: baseUrl, resolvingAgainstBaseURL: true)
components.queryItems = [ NSURLQueryItem(name: "name", value: "abc+def") ]
XCTAssertEqual(components!.string!, "http://www.example.com?connectionToken=abc%2Bdef")

失败!

输出等于:

http://www.example.com?connectionToken=abc+def 

不是

http://www.example.com?connectionToken=abc%2Bdef

我尝试过多种变体,而我似乎根本无法输出%2B

4 个答案:

答案 0 :(得分:19)

我在Radar 24076063的回答中解释了为什么它的工作原理(通过一些文本的清理):

  

' +'字符在查询组件中是合法的,因此不需要进行百分比编码。

     

有些系统会使用' +'作为一个空间,需要' +'要加百分比编码的加号字符。但是,这种两阶段编码(将加号转换为%2B然后将空格转换为加号)容易出错,因为它很容易导致编码问题。如果URL被规范化,它也会中断(URL的语法规范化包括删除所有不必要的百分比编码 - 请参阅rfc3986第6.2.2.2节)。

     

因此,如果您因为代码正在与之交谈的服务器而需要这种行为,那么您将自己处理额外的转换。这是一段代码,显示了您需要做的两种方式:

NSURLComponents *components = [[NSURLComponents alloc] init];
NSArray *items = [NSArray arrayWithObjects:[NSURLQueryItem queryItemWithName:@"name" value:@"Value +"], nil];
components.queryItems = items;
NSLog(@"URL queryItems: %@", [components queryItems]);
NSLog(@"URL string before: %@", [components string]);

// Replace all "+" in the percentEncodedQuery with "%2B" (a percent-encoded +) and then replace all "%20" (a percent-encoded space) with "+"
components.percentEncodedQuery = [[components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"] stringByReplacingOccurrencesOfString:@"%20" withString:@"+"];
NSLog(@"URL string after: %@", [components string]);

// This is the reverse if you receive a URL with a query in that form and want to parse it with queryItems
components.percentEncodedQuery = [[components.percentEncodedQuery stringByReplacingOccurrencesOfString:@"+" withString:@"%20"] stringByReplacingOccurrencesOfString:@"%2B" withString:@"+"];
NSLog(@"URL string back: %@", [components string]);
NSLog(@"URL queryItems: %@", [components queryItems]);
     

输出结果为:

URL queryItems: (
    "<NSURLQueryItem 0x100502460> {name = name, value = Value +}"
)

URL string before: ?name=Value%20+

URL string after: ?name=Value+%2B

URL string back: ?name=Value%20+

URL queryItems: (
    "<NSURLQueryItem 0x1002073e0> {name = name, value = Value +}"
)

答案 1 :(得分:9)

正如其他答案所述,默认情况下,“+”未在iOS上编码。但是,如果您的服务器需要进行编码,请按以下步骤操作:

var comps = URLComponents(url: self, resolvingAgainstBaseURL: true)
// a local var is needed to fix a swift warning about "overlapping accesses" caused by writing to the same property that's being read.
var compsCopy = comps
compsCopy?.queryItems = [URLQueryItem(name: "name", value: "abc+def")]
comps?.percentEncodedQuery = compsCopy?.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")

答案 2 :(得分:4)

当内容类型为application / x-www-form-urlencoded时,

+可能是有效字符,请参阅the link,因此NSURLComponents不对其进行编码。

Apple还提到this

  

RFC 3986指定哪些字符必须在其中进行百分比编码   查询URL的组件,但不是这些字符应该如何   解释。使用分隔的键值对是常见的   惯例,但没有规范标准化。所以,你   可能会遇到与其他实现的互操作性问题   遵循这一惯例。

     

潜在的互操作性问题的一个值得注意的例子是如何   处理加号(+)字符:

     

根据RFC 3986,加号是a中的有效字符   查询,并且不需要进行百分比编码。但是,根据   对于URI寻址的W3C建议,加号是保留的   作为查询字符串中空格的简写表示法(例如,   ?问候=你好+世界)。

     

如果URL查询组件包含根据RFC格式化的日期   3339在时区偏移量中带加号(例如,   2013-12-31T14:00:00 + 00:00),将加号解释为空格   导致时间格式无效。 RFC 3339指定日期应该如何   格式化,但不建议加号是否必须   在URL中编码的百分比。取决于实施接收   在此URL中,您可能需要抢先对加号进行百分比编码   字符。

     

作为替代方案,请考虑编码复杂和/或潜在的编码   有问题的数据采用更强大的数据交换格式,例如   JSON或XML。

结论是,您可能编码也可能不编码&#39; +&#39;。

在我看来,NSURLComponents只编码确保应编码的字符,例如&#39;&amp;&#39;,&#39; =&#39;或汉字,如&#39;你&#39; &#39;好&#39;,它不会根据内容类型编码或不编码字符,例如&#39; +&#39;我上面提到过。因此,如果您发现必须编码&#39; +&#39;或者您的服务器无法正确解析,您可以使用下面的代码。

我不太了解,所以我只提供了客观的代码,对不起。

- (NSString *)URLEncodingValue:(NSString *)value
{
    NSCharacterSet *set = [NSCharacterSet URLQueryAllowedCharacterSet];
    NSMutableCharacterSet *mutableQueryAllowedCharacterSet = [set mutableCopy];
    [mutableQueryAllowedCharacterSet removeCharactersInString:@"!*'();:@&=+$,/?%#[]"];
    return [value stringByAddingPercentEncodingWithAllowedCharacters:mutableQueryAllowedCharacterSet];
}

!*'();:@&=+$,/?%#[]是RFC 3986中定义的保留字符,代码将编码所有这些字符出现在value参数中,如果您只想编码&#39; +&#39;,只需替换{{1与!*'();:@&=+$,/?%#[]

答案 3 :(得分:1)

这是一个Apple bug。而是使用

NSString -stringByAddingPercentEncodingWithAllowedCharacters:

NSCharacterSet +URLQueryAllowedCharacterSet