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
相关产品推荐
相关产品推荐

