php - 如何在 swift 中 http 发布特殊字符

标签 php swift

我正在使用以下内容将电子邮件和密码发送到我的服务器(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 查询的值,这会留下一些百分比转义选项:

  1. 您可以构建自己的要转义字符的 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-._~/?")
        }
    
    }
    
  2. 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/

相关文章:

ios - 如何检测何时关闭 RPSystemBroadcastPickerView?

swift - 从 '[NSObject : AnyObject]?' 转换为无关类型 'NSDictionary' 总是失败

php - 在 phpmyadmin 中编辑的 mysql 数据库不会在 php 脚本中返回查询

php - 在 PHP 中使用 preg_match 或 preg_replace 将 MySql 数据库数据格式化或更改为 realpath

javascript - 带有预先转换的 json url 的 jQuery UI 自动完成

ios - 使用通知从后台唤醒 iOS 应用程序

ios - 我如何获得子字符串?

ios - BGTaskScheduler 提交调用失败,BGTaskSchedulerErrorDomain 代码为 : 1

php - Laravel 5.2 使用 Eloquent 方法获取每行数据的 date_diff

javascript - 从 PHP/JavaScript 发送到 Python 脚本