Swift 4 以 x-www-form-urlencoded 形式发送 POST 请求
Swift 4 以 x-www-form-urlencoded 形式发送 POST 请求
我想向我的php 7服务器发送一个POST请求,该服务器接受application/x-www-form-urlencoded
格式的数据。我要发送的数据是在一个结构体中,我想在提交时将该结构体的每个属性作为参数获取。
这是处理我的urlSession请求(包括GET和POST)的结构体
XHR.swift
struct XHR {
enum Result
case success(T)
case failure(Error)
}
func urlSession
let file = file.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// 设置URL请求
guard let url = URL.init(string: file) else {
print("错误:无法创建URL")
return
}
var urlRequest = URLRequest(url: url)
if method == "POST" {
urlRequest.httpMethod = "POST";
urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
urlRequest.httpBody = data
print(urlRequest.httpBody)
}
// 设置会话
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// 与let session = URLSession.shared不同
// 发送请求
let task = session.dataTask(with: urlRequest, completionHandler: {
(data, response, error) in
DispatchQueue.main.async { // 正确
guard let responseData = data else {
print("错误:未收到数据")
return
}
let decoder = JSONDecoder()
print(String(data: responseData, encoding: .utf8))
do {
let todo = try decoder.decode(T.self, from: responseData)
completionHandler(.success(todo))
} catch {
print("将数据转换为JSON时出错")
//print(error)
completionHandler(.failure(error))
}
}
})
task.resume()
}
}
这是发送POST请求到服务器的函数:
VideoViewModel.swift
struct User: Codable {
let username: String
let password: String
static func archive(w:User) -> Data {
var fw = w
return Data(bytes: &fw, count: MemoryLayout
}
static func unarchive(d:Data) -> User {
guard d.count == MemoryLayout
fatalError("BOOM!")
}
var w:User?
d.withUnsafeBytes({(bytes: UnsafePointer
w = UnsafePointer
})
return w!
}
}
enum Login {
case success(User)
case failure(Error)
}
func login(username: String, password: String, completionHandler: @escaping (Login) -> Void) {
let thing = User(username: username, password: password)
let dataThing = User.archive(w: thing)
xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result
switch result {
case .failure(let error):
completionHandler(.failure(error))
case .success(let user):
//let convertedThing = User.unarchive(d: user)
completionHandler(.success(user))
}
}
}
我这样调用它:
videoViewModel.login(username: "rexhin", password: "bonbon") { (result: VideoViewModel.Login) in
switch result {
case .failure(let error):
print("错误")
case .success(let user):
print(user)
}
}
从PHP中我可以看到成功提交了一个POST请求,但当我尝试通过$_POST["username"]
来获取username
字段时,我得到Undefined index:
的错误。
应用程序的完整代码可以在这里看到:https://gitlab.com/rexhin/ios-kida.git
问题的原因是需要发送一个x-www-form-urlencoded格式的POST请求,但是在Swift 4中默认的编码方式是JSON编码,因此需要找到一种方法来实现x-www-form-urlencoded编码。
解决方法是使用Alamofire / Vapor库中的URLEncodedFormEncoder.swift文件来进行编码。具体步骤如下:
1. 将URLEncodedFormEncoder.swift文件添加到你的项目中,该文件是来自Alamofire / Vapor库的自定义URLEncodedFormEncoder。
2. 让你的模型遵循Swift的Encodable协议,就像你在进行JSON编码时一样。
3. 使用与JSON编码时相同的方式对模型进行编码。
示例代码如下:
// 创建请求模型
let requstModel = OpenIDCTokenRequest(
clientId: clientId,
clientSecret: clientSecret,
username: username,
password: password
)
// 使用URLEncodedFormEncoder对模型进行编码
guard let requestData: Data = try? URLEncodedFormEncoder().encode(requstModel) else {
return // 处理编码错误
}
通过以上方法,我们可以在Swift 4中发送x-www-form-urlencoded格式的POST请求。
问题的原因是你将User.archive(w: thing)
的结果作为请求体中的data
,这可能永远不会成功。一般来说,你的archive(w:)
和unarchive(d:)
不会生成任何有用的结果,最好立即将它们移除。
如果你想传递需要x-www-form-urlencoded
的参数,你需要创建一个类似于URL查询的字符串。
可以尝试这样做:
func login(username: String, password: String, completionHandler: (Login) -> Void) {
let dataThing = "username=\(username)&password=\(password)".data(using: .utf8)
xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result<User>) in
//...
}
}
上面的例子有点过于简化了,如果username
和/或password
可能包含一些特殊字符,你可能需要在将它们嵌入字符串之前进行转义。你可以在网上找到很多关于这方面的文章。
是的,但是我每次发送POST请求都需要手动这样做。有没有办法将一个结构体转换为"username=\(username)&password=\(password)"
?
是的,需要手动转换。如果你想咨询关于任何结构体的问题,那是另一个问题。在这个问题中,你写的是this struct。
如果username或password的值需要进行URL编码,这种方法可能不起作用。
谢谢你的澄清。在The example above is ...部分中也有提到这一点,但你的评论更好一些。
问题的原因是作者在使用Swift 4发送POST请求时遇到了表单数据的问题,作者尝试了使用Alamofire库临时解决该问题。但最终作者找到了这段代码,并发现它可以通过纯编码的方式传递参数。感谢作者的分享。
解决方法是作者使用了Swift 4中的URLSession来发送POST请求。首先,作者创建了一个URL对象,用于指定请求的URL地址。然后,作者获取了用户名和密码的值,并将其拼接成x-www-form-urlencoded格式的字符串。接下来,作者创建了一个URLRequest对象,并设置了请求的方法为POST,设置了请求的Content-Type为application/x-www-form-urlencoded,并设置了Accept-Language字段。然后,作者将拼接好的数据作为请求的httpBody。然后,作者创建了一个URLSession对象,并使用该对象创建了一个dataTask,用于发送请求。在请求完成后的回调中,作者首先判断是否有错误发生,如果有则打印错误信息。然后,作者判断是否有响应数据,如果有则打印"here in response"。最后,作者将响应数据转换为字符串,并打印出来。
这段代码解决了作者在使用Swift 4发送POST请求时遇到的x-www-form-urlencoded表单数据的问题,通过纯编码的方式传递参数。作者将这段代码与我们分享,并表示这段代码对解决该问题非常有用。