PHP与Swift物品列表数组JSON序列化返回空元素问题排查
解决Swift请求PHP接口的类型转换与空数据问题
问题根源分析
你遇到的两个问题其实是同一个核心原因导致的:HTTP请求方式和后端参数接收逻辑不匹配
- 类型转换错误:服务器最终返回的是物品清单数组(对应JSON格式的
[]),但你强行将其转为NSDictionary(对应JSON的{}),自然会抛出类型不兼容的错误 - 空集合问题:你最初用GET请求,但PHP代码是通过
php://input接收JSON格式的请求体,GET请求的参数不会放入请求体中,导致后端拿不到userID,查询不到数据所以返回空数组
最终解决方案
把Swift的请求方法从GET改为POST,同时确保前后端参数传递逻辑一致,以下是修正后的完整代码和关键要点:
1. PHP接口代码(优化参数接收与SQL安全性)
inventoryCheck.php:调整了连接关闭的顺序(避免提前return导致连接未释放),保持用php://input接收POST请求的JSON体
<?php set_include_path('.:/testservice/'); $data = json_decode(file_get_contents('php://input'), true); $userID = $data["userID"]; // 若需临时测试GET请求,可取消注释下面一行,但正式环境建议用POST // $userID = htmlentities($_GET["userID"]); include 'Conn.php'; /* 数据库连接文件 */ include 'aSQLDao.php'; /* 业务逻辑文件 */ $dao = new aSQLDao(); $dao->openConnection(); $userInventory = $dao->getUserInventory($userID); echo json_encode($userInventory); $dao->closeConnection(); return; ?>
getUserInventory方法:优化为预处理语句绑定参数,避免SQL注入风险(原写法存在安全隐患)
public function getUserInventory($userID) { $returnValue = array(); // 用占位符替代直接拼接参数,防止SQL注入 $sql = "select aObjects.objectID, objectDescription from aUsers join aInventory on aUsers.userID = aInventory.userID join aObjects on aInventory.objectID = aObjects.objectID where aUsers.userID=?"; try { $statement = $this->conn->prepare($sql); $statement->bind_param("i", $userID); // 假设userID是整数类型,用"i"绑定参数 $statement->execute(); $result = $statement->get_result(); if ($result != null && (mysqli_num_rows($result) >= 1)) { $row = $result->fetch_all(MYSQLI_ASSOC); if (!empty($row)) { $returnValue = $row; } } } catch (PDOException $e) { print $e->getMessage(); } return $returnValue; }
2. Swift请求代码(修正请求方法与数据解析逻辑)
将请求方法改为POST,并把返回的JSON解析为数组类型(匹配后端返回的物品清单格式),同时补充了线程切换和空值判断:
@IBAction func inventoryButtonTapped(_ sender: Any) { let userID = "5" guard let myURL = URL(string: "http://myIP/testservice/inventoryCheck.php") else { displayMessage(userMessage: "无效的请求地址") return } var request = URLRequest(url: myURL) request.httpMethod = "POST" // 关键:将请求方式改为POST request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("application/json", forHTTPHeaderField: "Accept") let requestBody = ["userID": userID] as [String: String] do { request.httpBody = try JSONSerialization.data(withJSONObject: requestBody, options: []) } catch let error { print(error.localizedDescription) displayMessage(userMessage: "请求体构造失败") return } let task = URLSession.shared.dataTask(with: request) { data, response, error in // 处理请求错误 if let error = error { DispatchQueue.main.async { self.displayMessage(userMessage: "请求失败,请检查网络") } print("请求错误:\(error)") return } // 处理空数据情况 guard let data = data else { DispatchQueue.main.async { self.displayMessage(userMessage: "未接收到数据") } return } do { // 关键:解析为数组类型,而非字典 if let inventoryArray = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] { DispatchQueue.main.async { // 将数据赋值给UITableView数据源并刷新表格 self.inventoryDataSource = inventoryArray self.tableView.reloadData() } print("获取到的物品清单:\(inventoryArray)") } else { DispatchQueue.main.async { self.displayMessage(userMessage: "数据格式错误") } } } catch let error { print("JSON解析错误:\(error.localizedDescription)") DispatchQueue.main.async { self.displayMessage(userMessage: "数据解析失败") } } } task.resume() }
额外注意事项
- 线程安全:Swift中更新UI(如提示弹窗、刷新表格)必须切换回主队列,否则会导致崩溃
- 错误边界:增加URL有效性、数据非空等判断,能提升代码的健壮性
- 安全规范:永远不要直接将用户输入拼接到SQL语句中,预处理绑定参数是必须的安全操作
内容的提问来源于stack exchange,提问作者LostAngeles
相关产品推荐
相关产品推荐

