带有二进制正文的 iOS 快速发布请求

标签 ios swift nsurlrequest urlrequest

我想从 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 这样简单的东西之前,我不知道这是一个问题。此时我的 Tornado 服务器开始提示我正在发送它

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

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

最佳答案

两种常见的解决方案是:

  1. 您的错误消息告诉我们网络服务需要一个 x-www-form-urlencoded 请求(例如 key=value) value,您可以对二进制负载执行 base-64 编码。

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

    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 这样的库让它变得更容易,让您摆脱构建这些请求的杂草。

关于带有二进制正文的 iOS 快速发布请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38798273/

相关文章:

iphone - 分层 UIImageView 和 UIBezierPath

ios - fatal error ..从另一个类访问变量时

objective-c - 如何混合 objective-c 和 swift 制作 cocoapods 框架

ios - 如何重新发送电子邮件验证 parse.com

php - 如何在 JSON 中编码 MYSQL 响应并在 iOS 应用程序中解析

ios - 无法在我的 WKWebView POST 请求上设置 header

ios - Firebase 电话号码身份验证错误

ios - 我可以在 cordova 中为 iPad 和 iPhone(通用)应用程序设置不同的方向吗?

iphone - 如何使用 NSUndoManager 支持替换 UITextView 中的文本?

ios - 在同步调用上强制NSUrlConnection超时