how to http post special chars in swift

前端 未结 3 1347
[愿得一人]
[愿得一人] 2020-12-02 01:10

I\'m using the following to post an email and password to my server (php script). The problem I\'m having is the password contains a special char (specifically the &am

相关标签:
3条回答
  • 2020-12-02 01:33

    You need to percent escape the password before adding it to the URL. Better yet, use NSURLComponents to build your URL and add a query string.

    0 讨论(0)
  • 2020-12-02 01:46

    Here's some sample code you can try in a playground.

    //: 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
    
    0 讨论(0)
  • 2020-12-02 01:47

    You should be wary of using NSURLComponents because the NSURLQueryItem might percent-escape the character in question, the &, it doesn't percent-escape the + character (which PHP will interpret as a space in conformance with the W3C Specification for x-www-form-urlencoded). As the queryItems documentation says:

    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).

    This leaves a few alternatives for percent escaping the values you add to the query of the URL yourself if your value might include a + character:

    1. You can build your own CharacterSet of characters to be escaped and then use addingPercentEncodingForURLQueryValue in 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-._~/?")
          }
      
      }
      
    2. Alamofire takes a similar approach, but approaches this from the other direction, namely grabbing the .urlQueryAllowed character set (which is close, but not quite right), and takes out reserved characters identified in RFC 3986. In 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
      }
      

    You could then use the above to percent escape the key and the value in the body of the request, for example:

    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)
    

    For Swift 2 renditions of the above, see the previous revision of this answer.

    0 讨论(0)
提交回复
热议问题