【问题标题】:iOS swift post request with binary body带有二进制正文的 iOS swift post 请求
【发布时间】:2016-08-05 21:56:17
【问题描述】:

我想从 iOS (swift3) 发出一个 POST 请求,它将一大块原始字节作为正文传递。我做了一些实验,这让我认为以下方法有效:

let url = URL(string: "https://bla/foo/bar")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = Data(hex: "600DF00D")
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
    "DATA \(data ?? Data()) RESPONSE \(response) ERROR \(error)".print()
}
task.resume()

直到我尝试发送像单个 0xF0 这样简单的东西时才知道这是个问题。此时我的龙卷风服务器开始抱怨我正在发送它

WARNING:tornado.general:Invalid x-www-form-urlencoded body: 'utf-8' codec can't decode byte 0xf0 in position 2: invalid continuation byte

我只是应该以某种方式设置一些标题吗?还是我需要做些不同的事情?

【问题讨论】:

    标签: ios swift nsurlrequest urlrequest


    【解决方案1】:

    两种常见的解决方案是:

    1. 您的错误消息告诉我们,Web 服务正在等待 x-www-form-urlencoded 请求(例如 key=value),对于 value,您可以对二进制有效负载执行 base-64 编码。

      不幸的是,base-64 字符串仍然需要进行百分比转义(因为 Web 服务器通常将 + 字符解析为空格),因此您必须执行以下操作:

      let base64Encoded = data
          .base64EncodedString(options: [])
          .addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
          .data(using: String.Encoding.utf8)!
      
      var body = "key=".data(using: .utf8)!
      body.append(base64Encoded)
      
      var request = URLRequest(url: url)
      request.httpBody = body
      request.httpMethod = "POST"
      request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
      
      let task = URLSession.shared.dataTask(with: request) { data, response, error in
          guard error == nil else {
              print(error!)
              return
          }
      
          ...
      }
      task.resume()
      

      地点:

      extension CharacterSet {
          static let urlQueryValueAllowed: CharacterSet = {
              let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
              let subDelimitersToEncode = "!$&'()*+,;="
      
              var allowed = CharacterSet.urlQueryAllowed
              allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
              return allowed
          }()
      }
      

      有关该字符集的更多讨论,请参阅此答案中的第 2 点:https://stackoverflow.com/a/35912606/1271826

      无论如何,当您在服务器上收到此内容时,您可以将其检索为然后反转 base-64 编码,您将获得原始二进制有效负载。

    2. 或者,您可以使用multipart/formdata 请求(您可以在其中提供二进制有效负载,但您必须将其包装为更广泛的multipart/formdata 格式的一部分)。如果您想自己执行此操作,请参阅https://stackoverflow.com/a/26163136/1271826

    对于这两种方法,Alamofire 之类的库可以让您更轻松地摆脱构建这些请求的麻烦。

    【讨论】:

    • Base64 的效率并不高,但我最终决定,http-ville 中的文本流从来没有那么高效。只需一行代码即可将二进制数据转换为其 base64 版本。由于我将它用于正文,而不是 url,我什至不需要转义。
    猜你喜欢
    • 1970-01-01
    • 2013-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2015-08-14
    • 2016-09-29
    相关资源
    最近更新 更多