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

Swift POST提交数据到PHP写入MySQL报数据格式错误

问题描述

Xcode环境下开发支持笔记上传至Web服务器的应用,POST方式提交数据时抛出错误:The data couldn’t be read because it isn’t in the correct format.
调试确认:

  • 测试参数为["title": "Fgh", "post": "Fgg"]时,JSON序列化后数据长度28字节
  • 请求无网络层面错误,HTTP状态码返回200,响应头Content-Type为text/html; charset=UTF-8,返回数据长度235字节
  • JSON解码阶段固定触发格式错误
根本原因
  • 传输格式不匹配:Swift端设置请求头为application/json,提交JSON格式请求体,但PHP端默认通过$_POST仅能接收application/x-www-form-urlencoded格式的表单数据,无法获取上传的title、post参数,会抛出Undefined array key级别的PHP警告,污染返回内容
  • 返回格式不符合预期:PHP端输出纯文本/HTML拼接内容,未返回JSON格式数据,Swift端强行调用JSONDecoder解码必然失败
  • 代码逻辑错误:原PHP代码mysqli_query($query, $connection)参数顺序颠倒,数据库执行本身就会报错;且直接拼接SQL语句存在SQL注入风险
  • 解码逻辑错误:Swift端解码目标类型为[postDataBase].self(笔记数组),但单条上传场景不存在数组返回结构;且postDataBase要求必传ID字段,原逻辑中无对应返回值
  • 线程风险:原代码用DispatchQueue.main.sync回调主线程,存在主线程死锁概率
修复方案

PHP端调整

替换post.php代码如下:

<?php
header('Content-Type: application/json');
$response = [
    'code' => 0,
    'msg' => '',
    'data' => null
];

// 读取JSON格式原始请求体
$rawPost = file_get_contents('php://input');
$params = json_decode($rawPost, true);
if (!$params || !isset($params['title']) || !isset($params['post'])) {
    $response['code'] = 400;
    $response['msg'] = '参数缺失';
    echo json_encode($response);
    exit;
}
$title = $params['title'];
$post = $params['post'];

// 连接数据库,选择库
$connection = mysqli_connect("localhost","****","****","mlbroadv_PlantAssistDB");
if(!$connection) {
    $response['code'] = 500;
    $response['msg'] = '数据库连接失败: ' . mysqli_connect_error();
    echo json_encode($response);
    exit;
}

// 预处理语句防注入
$stmt = mysqli_prepare($connection, "INSERT INTO Notes (title, post) VALUES (?, ?)");
mysqli_stmt_bind_param($stmt, "ss", $title, $post);
$execResult = mysqli_stmt_execute($stmt);

if ($execResult) {
    $response['code'] = 200;
    $response['msg'] = '上传成功';
    $response['data'] = [
        'ID' => (string)mysqli_insert_id($connection),
        'title' => $title,
        'post' => $post
    ];
} else {
    $response['code'] = 500;
    $response['msg'] = '插入失败: ' . mysqli_stmt_error($stmt);
}

mysqli_stmt_close($stmt);
mysqli_close($connection);
echo json_encode($response);
?>

调整点:

  • 强制设置响应头为application/json,所有场景返回标准JSON结构
  • 用php://input读取原始请求体,解析JSON参数,适配Swift端提交格式
  • 修正mysqli函数调用顺序,用预处理语句避免SQL注入
  • 插入成功后返回自增ID,匹配Swift端结构体字段要求
  • 所有异常分支统一返回JSON格式,避免输出PHP警告/纯文本内容

Swift端调整

首先修正数据模型,匹配接口返回结构:

// 通用接口返回结构
struct ApiResponse<T: Decodable>: Decodable {
    let code: Int
    let msg: String
    let data: T?
}

struct postDataBase: Decodable {
    var ID: String
    var title: String
    var post: String
}

替换createPost方法代码:

func createPost(parameters: [String: Any]) {
    guard let url = URL(string: "\(prefixUrl)/post.php") else {
        print("URL初始化失败")
        return
    }
    
    guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters) else {
        print("参数JSON序列化失败")
        return
    }
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = httpBody
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("请求错误:", error.localizedDescription)
            return
        }
        // 调试用:打印服务端原始返回
        if let data = data, let rawReturn = String(data: data, encoding: .utf8) {
            print("服务端原始返回:", rawReturn)
        }
        do {
            guard let data = data else {
                print("无返回数据")
                return
            }
            // 解码为单条笔记结构,不是数组
            let result = try JSONDecoder().decode(ApiResponse<postDataBase>.self, from: data)
            DispatchQueue.main.async {
                if result.code == 200, let note = result.data {
                    print("上传成功,笔记信息:", note)
                } else {
                    print("业务错误:", result.msg)
                }
            }
        } catch {
            print("JSON解析错误:", error.localizedDescription)
            debugPrint(error)
        }
    }.resume()
}

调整点:

  • 去掉强制try!的崩溃风险,增加序列化容错
  • 新增通用接口返回层,匹配PHP端返回结构,解码目标从[postDataBase].self改为单条对象结构
  • 替换DispatchQueue.main.sync为async,避免主线程死锁
  • 增加原始返回内容打印,后续调试可直接看到服务端输出,快速定位问题
验证步骤
  • 先单独访问post.php路径,确认无PHP语法错误
  • 触发上传时查看控制台打印的原始返回内容,确认是合法JSON格式
  • 确认数据库Notes表ID字段为自增主键,无需前端传值

内容的提问来源于stack exchange,提问作者FrosyFeet456

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:57:13