[iOS] URL 百分号编码

2,085 阅读1分钟

URL 百分号编码

百分号编码用%加两位十六进制数字来表示字符对应的二进制值的一个字节。为了比较的一致性,十六进制数字应统一使用大写字母 A-F

URL 在传输时,只能由基于 ASCII 字符集的保留字符、非保留字符以及用于指明编码的%组成,其他字符则需要百分号编码。

保留字符是指那些可能用于分割 URL 各个组件及其子组件的,即有特殊含义的字符。RFC 3986 section 2.2 中定义的保留字符如下:

  reserved    = gen-delims / sub-delims

  gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@"

  sub-delims  = "!" / "$" / "&" / "'" / "(" / ")"
              / "*" / "+" / "," / ";" / "="

非保留字符没有特殊含义,根据 RFC 3986 section 2.3,包括:大小写字母、数字、-._~

如果组件内容中包含了保留字符,为了防止解析错误,要在组件被组装前,对作为具体内容的保留字符进行百分号编码。而如果组件内的保留字符没有了特殊含义,如 query 中的/,则不需要被编码,因为这并不改变整个 URL 的意义。同理,虽然非保留字符与其对应的编码表示的意义是一样的,但出于一致性的考虑,非保留字符也不应该被编码。

百分号编码时,保留字符使用 ASCII 编码的二进制表示,其他字符使用 utf-8 编码的二进制表示。如果%本身要作为数据,则会被编码为%25表示。

HTML 表单

HTML 表单将键值对数据通过 HTTP 的 Get 或 Post 方法提交到服务器,而 W3C 对表单采用了老版本的编码规则,会将+视为空格的编码字符。所以客户端应将+也进行百分号编码,以防止服务器的这一特殊规则导致的解析错误。

完整的 URL 字符串进行百分号编码

参考 JavaScript 的 encodeURI 方法 ,如果是对完整的 URL 字符串进行编码操作,那就默认为其中有特殊含义的保留字符都是不需要编码的,只是将 URL 可用字符以外的字符编码,且认为其中没有已编码的部分,%会被编码为%25

- (NSString *)encodedURLString {
    NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
    NSString *unreservedSymbols = @"-._~";
    [allowedCharacters addCharactersInString:unreservedSymbols];
    NSString *allowedSymbols = @":/?#[]@!$&'()*,;="; // 不需要编码的符号中不包括 +,是针对服务器将 + 解析为空格的情况。
    [allowedCharacters addCharactersInString:allowedSymbols];
    
    return [self stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
}

解码

- (NSString *)decodedURLString {
    // 方法 [- stringByRemovingPercentEncoding] 只解码百分号编码的字符,而对于服务器将空格编码为 + 的情况,则要额外处理。
    NSString *preprocessedString = [self stringByReplacingOccurrencesOfString:@"+" withString:@" "];
    
    return [preprocessedString stringByRemovingPercentEncoding];
}

参考