Я использую следующее, чтобы отправить электронное письмо и пароль на мой сервер (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
может уклониться от указанного символа, &
не имеет процентного NSURLComponents
от NSURLQueryItem
+
(который PHP будет интерпретировать как пространство в соответствии со спецификацией W3C для x-www-form-urlencoded
). Поскольку документация queryItems
гласит:
Заметка
RFC 3986 указывает, какие символы должны быть закодированы в процентах в компоненте запроса URL-адреса, но не должны интерпретировать эти символы. Использование разделительных пар ключ-значение является общим соглашением, но не стандартизировано спецификацией. Таким образом, вы можете столкнуться с проблемами совместимости с другими реализациями, которые следуют этому соглашению.
Одним из примечательных примеров потенциальных проблем взаимодействия является то, как обрабатывается символ знака плюс (
+
):Согласно RFC 3986 знак плюса является допустимым символом в запросе и не нуждается в процентном кодировании. Однако, согласно рекомендациям W3C для адресации URI , знак плюса зарезервирован как сокращенная запись для пробела в строке запроса (например, «
?greeting=hello+world
).
Это оставляет несколько альтернатив для процента, ускоряя значения, которые вы добавляете к запросу URL-адреса, если ваше значение может включать символ +
:
Вы можете создать свой собственный CharacterSet
символов для экранирования, а затем использовать addingPercentEncodingForURLQueryValue
в Swift 3:
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
набора символов .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)
Для Свифт 2 изложения вышеизложенного см. Предыдущую ревизию этого ответа .
Вам необходимо уклониться от пароля, прежде чем добавлять его в URL-адрес. Еще лучше, используйте NSURLComponents для создания вашего URL-адреса и добавления строки запроса.
Вот пример кода, который вы можете попробовать на игровой площадке.
//: Playground - noun: a place where people can play import UIKit var n = NSURLComponents() let s = "blah" // Note that the values here should not be % encoded let item1 = NSURLQueryItem(name: "password1", value: "ab%&") let item2 = NSURLQueryItem(name: "password2", value: "ab%&%#$%^&*(") n.queryItems = [item1, item2] n.URL?.query n.queryItems?.count n.queryItems![0].name n.queryItems![0].value n.queryItems![1].name n.queryItems![1].value n.URL