
本文详解 Flutter 调用 PHP 接口时因响应非标准 JSON 导致 FormatException: Unexpected character 的根本原因,并提供安全、规范的前后端联调修复方案。
本文详解 flutter 调用 php 接口时因响应非标准 json 导致 `formatexception: unexpected character` 的根本原因,并提供安全、规范的前后端联调修复方案。
在 Flutter 开发中,通过 http.post 请求 PHP 接口后调用 jsonDecode(response.body) 报错 Unhandled Exception: FormatException: Unexpected character (at character 1),本质是 PHP 返回的内容并非合法 JSON 字符串——常见于直接 echo json_encode("success") 或混入 HTML/空白/错误提示等非 JSON 内容(如
),导致 Dart 解析器在首字符处崩溃。
? 根本问题分析
-
❌ 错误 PHP 写法:
echo json_encode("success"); // 输出: "success"(合法 JSON 字符串,但无键名,且易受干扰)实际响应可能被 PHP 错误、BOM 头、调试 echo、HTML 换行符(如
)污染,导致返回类似:<br> "success"
此时 jsonDecode() 尝试解析
开头的字符串,立即抛出 Unexpected character (at character 1)。 -
❌ Flutter 端逻辑缺陷:
var data = jsonDecode(response.body); if (data == "success") { ... } // ❌ data 是 String 类型,但未做类型校验与异常防护
✅ 正确实践:前后端协同规范
1. PHP 端:输出纯净、结构化 JSON
<?php header('Content-Type: application/json; charset=utf-8'); // 强制 JSON 响应头
header('Access-Control-Allow-Origin: *'); // 开发阶段允许跨域(生产环境请限制)
// 数据库连接(示例,需替换为实际配置)
$host = 'localhost';
$dbname = 'your_db';
$user = 'root';
$pass = '';
$db = new mysqli($host, $user, $pass, $dbname);
if ($db->connect_error) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => 'DB connection failed']);
exit;
}
// ✅ 严格过滤输入(防 SQL 注入!)
$email = $db->real_escape_string($_POST['email'] ?? '');
$passworda = $db->real_escape_string($_POST['pass'] ?? ''); // 注意:前端传的是 'pass',非 'passworda'
// ✅ 使用参数化查询(强烈推荐替代 real_escape_string)
$stmt = $db->prepare("SELECT id FROM user WHERE email = ? AND passworda = ?");
$stmt->bind_param("ss", $email, $passworda);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows >= 1) {
echo json_encode(['status' => 'success', 'message' => 'Login OK']);
} else {
echo json_encode(['status' => 'error', 'message' => 'Invalid credentials']);
}
?>
⚠️ 关键改进:
- 设置 Content-Type: application/json 响应头;
- 统一返回结构化对象(非原始字符串);
- 修正字段名匹配(PHP 接收 $_POST['pass'],非 $_POST['passworda']);
- 添加基础错误处理与 HTTP 状态码。
2. Flutter 端:健壮解析 + 异常防护
Future<void> login(BuildContext context) async {
if (email.text.isEmpty || password.text.isEmpty) {
Fluttertoast.showToast(
msg: "Please complete all fields!",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
fontSize: 16.0,
);
return;
}
try {
final url = Uri.parse('http://192.168.43.150/v1_flutter/lib/php/connection.php');
final response = await http.post(
url,
body: {
'email': email.text,
'pass': password.text,
},
headers: {
'Accept': 'application/json', // ✅ 修正拼写:'applicarion/json' → 'application/json'
},
);
// ✅ 检查 HTTP 状态码
if (response.statusCode != 200) {
throw Exception('HTTP ${response.statusCode}: ${response.reasonPhrase}');
}
// ✅ 安全解析 JSON
final jsonData = jsonDecode(response.body) as Map<string dynamic>;
// ✅ 严格校验响应结构
if (jsonData.containsKey('status') && jsonData['status'] == 'success') {
Navigator.pop(context);
Navigator.pushNamed(context, '/registre');
} else {
final message = jsonData['message'] ?? 'Login failed';
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
fontSize: 16.0,
);
}
} on FormatException catch (e) {
// ✅ 捕获 JSON 解析失败(如返回 HTML/空/乱码)
print('JSON Parse Error: ${e.toString()}');
Fluttertoast.showToast(
msg: 'Server response invalid. Please check network.',
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
);
} on Exception catch (e) {
print('Network or Server Error: ${e.toString()}');
Fluttertoast.showToast(
msg: 'Connection failed: ${e.toString()}',
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.BOTTOM,
);
}
}</string></void>
✅ 关键加固点:
- 使用 try/catch 捕获 FormatException 和通用 Exception;
- 验证 response.statusCode;
- 强制类型转换 as Map
提升类型安全; - 通过键值('status')而非原始字符串比较判断结果;
- 修正请求头拼写错误(application/json)。
? 总结与最佳实践
- 永远不要直接 echo json_encode("string"):返回结构化对象(如 {'status': 'success'}),便于扩展与维护;
- PHP 必须设置 header('Content-Type: application/json'),避免浏览器/服务器注入额外内容;
- Flutter 必须包裹 jsonDecode 在 try/catch 中,并检查 statusCode;
- 严禁 SQL 拼接:使用预处理语句(prepare/bind_param)防御注入;
-
开发调试技巧:在 Flutter 中打印 response.body 原始内容,确认 PHP 是否返回纯 JSON:
print('Raw response: ${response.body}');
遵循以上规范,即可彻底解决 FormatException: Unexpected character 问题,并构建安全、可维护的 Flutter-PHP 交互链路。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











