Swift 4发送x-www-form-urlencoded格式POST请求至PHP7服务器问题
解决Swift结构体转application/x-www-form-urlencoded POST参数问题
看起来你已经成功把POST请求发送到PHP服务器了,但应该是结构体属性没有正确转换成符合格式的请求参数,导致PHP端拿不到预期数据对吧?我来给你一步步解决这个问题:
1. 让你的结构体支持转成键值对字典
首先,我们需要给数据结构体添加一个方法,把它的所有属性转换成[String: Any]字典,这样后续才能编码成x-www-form-urlencoded格式。假设你的结构体是这样的:
struct VideoData: Codable { var title: String var duration: Int var isPublic: Bool // 其他自定义属性... }
给它扩展一个转字典的方法:
extension VideoData { func toDictionary() -> [String: Any] { let mirror = Mirror(reflecting: self) var paramDict = [String: Any]() for child in mirror.children { guard let key = child.label else { continue } paramDict[key] = child.value } return paramDict } }
这个方法通过反射把结构体的属性名和值对应成字典,适合简单的结构体;如果有嵌套结构体,需要额外做递归处理。
2. 把字典编码成x-www-form-urlencoded格式字符串
接下来要把字典转换成key1=value1&key2=value2格式的字符串,同时处理特殊字符的URL编码:
func encodeToFormData(dictionary: [String: Any]) -> String? { var components = [(String, String)]() for (key, value) in dictionary { // 把值转成字符串并做URL编码,避免特殊字符破坏格式 guard let stringValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let encodedKey = key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { continue } components.append((encodedKey, stringValue)) } return components.map { "\($0)=\($1)" }.joined(separator: "&") }
3. 修改XHR.swift的POST请求实现
确保你的POST请求设置了正确的Content-Type头,并且把编码后的字符串作为HTTPBody。比如XHR.swift里的POST方法可以改成这样:
class XHR { static func post(url: String, parameters: [String: Any], completion: @escaping (Data?, URLResponse?, Error?) -> Void) { guard let requestUrl = URL(string: url) else { completion(nil, nil, NSError(domain: "Invalid URL", code: -1, userInfo: nil)) return } var request = URLRequest(url: requestUrl) request.httpMethod = "POST" // 必须设置正确的Content-Type,PHP端才能识别为表单数据 request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") // 把参数编码成form data格式并设置为请求体 if let formDataString = encodeToFormData(dictionary: parameters) { request.httpBody = formDataString.data(using: .utf8) } let task = URLSession.shared.dataTask(with: request, completionHandler: completion) task.resume() } }
4. 在VideoViewModel中调用
现在你可以把结构体转成字典后传给XHR的POST方法:
class VideoViewModel { func submitVideoData() { let videoData = VideoData(title: "Test Video", duration: 120, isPublic: true) let requestParams = videoData.toDictionary() XHR.post(url: "http://your-php-server/api/submit.php", parameters: requestParams) { data, response, error in // 处理请求响应 if let error = error { print("Request failed: \(error.localizedDescription)") return } // 解析PHP返回的数据 if let data = data, let responseStr = String(data: data, encoding: .utf8) { print("PHP Response: \(responseStr)") } } } }
5. PHP端接收参数
确保PHP端能正确获取参数,这里有两种可靠方式:
- 如果Content-Type设置正确,直接用
$_POST获取:
<?php $title = $_POST['title'] ?? 'No title'; $duration = (int)($_POST['duration'] ?? 0); $isPublic = $_POST['isPublic'] === "true" ? true : false; // 处理业务逻辑... echo json_encode(['status' => 'success', 'received_data' => $_POST]); ?>
- 若偶尔出现
$_POST拿不到数据的情况(比如编码异常),可以读取原始请求体:
<?php $rawRequest = file_get_contents('php://input'); parse_str($rawRequest, $postData); $title = $postData['title'] ?? 'No title'; // 处理业务逻辑... ?>
注意事项
- 如果结构体中有嵌套结构体或数组,上面的
toDictionary方法需要递归处理,否则嵌套对象会被转成无意义的字符串(比如[Object]),PHP端无法解析。 - 务必确保所有字符串都做了URL编码,不然&、=、空格这类特殊字符会破坏form data的格式。
内容的提问来源于stack exchange,提问作者M1X
相关产品推荐
相关产品推荐

