You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:18:18