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

Flutter对接PHP登录接口报FormatException解析异常如何解决

Flutter对接PHP登录接口JSON解析异常修复

问题表现

开发Flutter登录功能对接PHP后端接口时,触发未捕获异常导致登录流程中断:

  • 控制台抛出FormatException,提示响应内容第1个字符为非法字符
  • 日志显示接口返回内容开头存在<br />HTML标签,导致jsonDecode方法解析响应体失败
    核心报错日志如下:

E/flutter ( 6084): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)]
Unhandled Exception: FormatException: Unexpected character (at character 1)
E/flutter ( 6084):

E/flutter ( 6084): ^
E/flutter ( 6084):
E/flutter ( 6084): #0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1383:5)
E/flutter ( 6084): #1 _ChunkedJsonParser.parseNumber (dart:convert-patch/convert_patch.dart:1250:9)
E/flutter ( 6084): #2 _ChunkedJsonParser.parse (dart:convert-patch/convert_patch.dart:915:22)
E/flutter ( 6084): #3 _parseJson (dart:convert-patch/convert_patch.dart:35:10)
E/flutter ( 6084): #4 JsonDecoder.convert (dart:convert/json.dart:612:36)
E/flutter ( 6084): #5 JsonCodec.decode (dart:convert/json.dart:216:41)
E/flutter ( 6084): #6 jsonDecode (dart:convert/json.dart:155:10)
E/flutter ( 6084): #7 Login.login (package:mes_v1/pages/Authentification/login.dart:25:18)

问题根因

报错核心原因是PHP接口返回内容混入了HTML格式的错误提示,不是纯JSON格式,同时前后端代码存在多处不匹配和逻辑漏洞:

  • PHP端问题:
    • 变量名不一致:接收表单密码参数时赋值给$passworda,但SQL拼接时使用未定义的$password变量,PHP会自动抛出Notice级错误,错误内容默认以<br />标签开头,直接输出在JSON响应前
    • 未提前设置JSON响应头,未关闭前端错误输出,无法保证返回内容为纯JSON格式
    • 直接拼接用户输入到SQL语句,存在严重SQL注入风险
  • Flutter端问题:
    • 参数字段不匹配:POST请求传的密码字段是pass,和PHP端接收的passworda字段名不一致
    • 请求头拼写错误:Accept头值将application/json错写为applicarion/json
    • 无异常捕获和响应校验逻辑:未判断HTTP请求状态,未捕获JSON解析异常,遇到非预期响应直接崩溃

修复方案

PHP端修复

首先统一响应格式,修正变量名错误,关闭前端错误输出,改用预处理语句防范SQL注入,修复后代码如下:

<?php
// 强制设置JSON响应头,指定编码
header('Content-Type: application/json; charset=utf-8');
// 关闭前端错误显示,避免HTML格式错误信息混入JSON响应,生产环境建议开启日志记录错误
ini_set('display_errors', 0);
error_reporting(0);

// 补全自己的数据库连接逻辑,保证$db为有效的mysqli连接实例
$email = isset($_POST['email']) ? trim($_POST['email']) : '';
$password = isset($_POST['passworda']) ? trim($_POST['passworda']) : '';

// 参数为空直接返回错误
if (empty($email) || empty($password)) {
    echo json_encode("error");
    exit;
}

// 使用预处理语句查询,避免SQL注入
$stmt = mysqli_prepare($db, "SELECT * FROM user WHERE email = ? AND passworda = ?");
mysqli_stmt_bind_param($stmt, "ss", $email, $password);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$count = mysqli_num_rows($result);

if ($count >= 1) {
    echo json_encode("success");
} else {
    echo json_encode("error");
}
exit;
?>

Flutter端修复

修正请求参数名、请求头拼写错误,增加HTTP状态校验和异常捕获逻辑,修复后代码如下:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart' as http;

class Login extends StatelessWidget {
  final TextEditingController email = TextEditingController();
  final TextEditingController password = TextEditingController();

  Future login(BuildContext cont) async {
    if (email.text.isEmpty || password.text.isEmpty) {
      Fluttertoast.showToast(
        msg: "请填写完整登录信息!",
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        fontSize: 16.0,
      );
      return;
    }
    // 捕获所有请求、解析环节的异常,避免页面崩溃
    try {
      var url = "http://192.168.43.150/v1_flutter/lib/php/connection.php";
      var response = await http.post(
        Uri.parse(url),
        body: {
          "email": email.text.trim(),
          // 字段名和PHP端保持一致
          "passworda": password.text.trim(),
        },
        // 修正Accept头拼写错误
        headers: {"Accept": "application/json"},
      );
      // 先校验HTTP响应状态
      if (response.statusCode != 200) {
        Fluttertoast.showToast(
          msg: "网络请求异常,请稍后重试",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          fontSize: 16.0,
        );
        return;
      }
      // 状态正常再解析JSON
      var data = jsonDecode(response.body);
      if (data == "success") {
        Navigator.pop(cont);
        Navigator.pushNamed(cont, "/registre");
      } else {
        Fluttertoast.showToast(
          msg: "账号或密码错误!",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          fontSize: 16.0,
        );
      }
    } catch (e) {
      Fluttertoast.showToast(
        msg: "登录失败,请稍后重试",
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        fontSize: 16.0,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    // 保留原有登录页布局代码即可
    return Scaffold();
  }
}

额外注意事项

  • 禁止明文存储用户密码,生产环境必须使用password_hash等不可逆哈希算法处理密码后存储,比对时使用password_verify验证
  • Android 9及以上版本默认禁止HTTP明文请求,调试时需要在应用配置中放开明文网络权限,生产环境建议改用HTTPS协议
  • 建议统一接口返回结构,例如固定返回{"code":业务状态码,"msg":"提示信息","data":业务数据}格式,方便前端做统一拦截处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:22:20