我正在使用以下内容将电子邮件和密码发送到我的服务器(php 脚本)。我遇到的问题是密码包含一个特殊的字符(特别是 &
符号),它似乎被剥离了。我想是因为它认为它的分隔变量被传递了。我如何在不剥离它的情况下传递这个字符?
let myURL = NSURL(string: "my script url here")
let request = NSMutableURLRequest(URL: myURL!)
request.HTTPMethod = "POST"
let postString = "email=\(userEmailText)&password=\(userPasswordText)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
最佳答案
你应该小心使用 NSURLComponents
因为 NSURLQueryItem
可能会百分号转义有问题的字符,&
,它不会百分比转义 +
字符(PHP 将解释为符合 W3C Specification for x-www-form-urlencoded
的空格)。正如 the queryItems
documentation 所说:
Note
RFC 3986 specifies which characters must be percent-encoded in the query component of a URL, but not how those characters should be interpreted. The use of delimited key-value pairs is a common convention, but isn't standardized by a specification. Therefore, you may encounter interoperability problems with other implementations that follow this convention.
One notable example of potential interoperability problems is how the plus sign (
+
) character is handled:According to RFC 3986, the plus sign is a valid character within a query, and doesn't need to be percent-encoded. However, according to the W3C recommendations for URI addressing, the plus sign is reserved as shorthand notation for a space within a query string (for example,
?greeting=hello+world
).
如果您的值可能包含 +
字符,那么对于您自己添加到 URL 查询的值,这会留下一些百分比转义选项:
您可以构建自己的要转义字符的
CharacterSet
,然后在 Swift 3 中使用addingPercentEncodingForURLQueryValue
:extension String { /// Returns a new string made from the `String` by replacing all characters not in the unreserved /// character set (as defined by RFC3986) with percent encoded characters. func addingPercentEncodingForURLQueryValue() -> String? { let allowedCharacters = CharacterSet.urlQueryValueAllowed() return addingPercentEncoding(withAllowedCharacters: allowedCharacters) } } extension CharacterSet { /// Returns the character set for characters allowed in the individual parameters within a query URL component. /// /// The query component of a URL is the component immediately following a question mark (?). /// For example, in the URL `http://www.example.com/index.php?key1=value1#jumpLink`, the query /// component is `key1=value1`. The individual parameters of that query would be the key `key1` /// and its associated value `value1`. /// /// According to RFC 3986, the set of unreserved characters includes /// /// `ALPHA / DIGIT / "-" / "." / "_" / "~"` /// /// In section 3.4 of the RFC, it further recommends adding `/` and `?` to the list of unescaped characters /// for the sake of compatibility with some erroneous implementations, so this routine also allows those /// to pass unescaped. static func urlQueryValueAllowed() -> CharacterSet { return CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~/?") } }
Alamofire 采用了类似的方法,但从另一个方向进行,即抓取
.urlQueryAllowed
字符集(接近但不太正确),并取出保留字符在 RFC 3986 中确定。在 Swift 3 中:/// Returns a percent-escaped string following RFC 3986 for a query string key or value. /// /// RFC 3986 states that the following characters are "reserved" characters. /// /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/" /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=" /// /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/" /// should be percent-escaped in the query string. /// /// - parameter string: The string to be percent-escaped. /// /// - returns: The percent-escaped string. public func escape(_ string: String) -> String { let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 let subDelimitersToEncode = "!$&'()*+,;=" var allowedCharacterSet = CharacterSet.urlQueryAllowed allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") var escaped = "" //========================================================================================================== // // Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few // hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no // longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more // info, please refer to: // // - https://github.com/Alamofire/Alamofire/issues/206 // //========================================================================================================== if #available(iOS 8.3, *) { escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string } else { let batchSize = 50 var index = string.startIndex while index != string.endIndex { let startIndex = index let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex let range = startIndex..<endIndex let substring = string.substring(with: range) escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring index = endIndex } } return escaped }
然后您可以使用上面的代码对请求正文中的键和值进行百分比转义,例如:
let parameters = ["email" : email, "password" : password]
request.httpBody = parameters
.map { (key, value) in
let escapedKey = key.addingPercentEncodingForURLQueryValue()!
let escapedValue = value.addingPercentEncodingForURLQueryValue()!
return "\(escapedKey)=\(escapedValue)"
}
.joined(separator: "&")
.data(using: .utf8)
有关上述内容的 Swift 2 版本,请参阅 previous revision of this answer。
关于php - 如何在 swift 中 http 发布特殊字符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35905347/