问题描述
我想从iOS(swift3)发出POST请求,该请求将原始字节的一部分作为正文传递.我做了一些实验,使我认为以下工作有效:
I want to make a POST request from iOS (swift3) which passes a chunk of raw bytes as the body. I had done some experimenting which made me thought the following worked:
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)之前,我都不知道这是一个问题.这时我的龙卷风服务器开始抱怨我正在发送它
Didn't know it was a problem until I tried sending something simple like a single 0xF0. At which point my tornado server started complaining that I was sending it
WARNING:tornado.general:Invalid x-www-form-urlencoded body: 'utf-8' codec can't decode byte 0xf0 in position 2: invalid continuation byte
我只是应该以某种方式设置一些标题吗?还是我需要做些不同的事情?
Am I just supposed to set some header somehow? Or is there something different I need to do?
推荐答案
两个常见的解决方案是:
The two common solutions are:
-
您的错误消息告诉我们Web服务正在请求
x-www-form-urlencoded
请求(例如key=value
),对于value
,您可以对二进制有效负载执行base-64编码.
Your error message tells us that the web service is expecting a
x-www-form-urlencoded
request (e.g.key=value
) and in for thevalue
, you can perform a base-64 encoding of the binary payload.
不幸的是,base-64字符串仍然需要转义百分比(因为Web服务器通常将+
字符解析为空格),因此您必须执行以下操作:
Unfortunately, base-64 strings still need to be percent escaped (because web servers generally parse +
characters as spaces), so you have to do something like:
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 .
For more discussion on that character set, see point 2 in this answer: https://stackoverflow.com/a/35912606/1271826.
无论如何,当您在服务器上收到此消息时,可以将其检索为,然后反转base-64编码,您将拥有原始的二进制有效负载.
Anyway, when you receive this on your server, you can retrieve it as and then reverse the base-64 encoding, and you'll have your original binary payload.
或者,您可以使用multipart/formdata
请求(可以提供二进制有效负载,但必须将其包装为更广泛的multipart/formdata
格式的一部分).如果您想自己执行此操作,请参见 https://stackoverflow.com/a/26163136/1271826 .
Alternatively, you can use multipart/formdata
request (in which you can supply binary payload, but you have to wrap it in as part of the broader multipart/formdata
format). See https://stackoverflow.com/a/26163136/1271826 if you want to do this yourself.
对于这两种方法, Alamofire 之类的库都使它变得更加容易,使您摆脱了构造这些请求的杂草.
For both of these approaches, libraries like Alamofire make it even easier, getting you out of the weeds of constructing these requests.
这篇关于iOS快速发布带有二进制正文的请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!