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

Flutter http.post提交数据超时但数据仍插入的问题优化咨询

优化方案

你遇到的是典型的「请求已被服务器处理,但客户端因超时未收到响应」的场景,核心原因是POST请求非幂等+无法确认服务器处理状态。以下是针对性的代码优化方案:

1. 实现幂等请求(核心解决方案)

给每个请求生成唯一标识(Request ID),服务器先校验该标识是否已处理,从根源避免重复插入。

Flutter端修改:

生成唯一UUID作为请求ID,随请求体发送:

import 'package:uuid/uuid.dart';

postData() async {
  const url = 'https://www.testdomain.com/app/submit.php';
  // 生成唯一请求ID
  final requestId = const Uuid().v4();
  try {
    final response = await http.post(Uri.parse(url), body: {
      "id": user,
      "notes": notesController.text,
      "request_id": requestId // 添加唯一请求ID
    }).timeout(const Duration(seconds: 4), onTimeout: () {
      return http.Response('Error', 408);
    });

    if (response.statusCode == 408) {
      // 超时后触发状态校验,见方案2
      _checkRequestStatus(requestId);
    } else if (response.statusCode == 200) {
      final auth = json.decode(response.body);
      if (!mounted) return;
      setState(() {
        success = auth['success'].toString();
        status = auth['status'].toString();
      });
    } else {
      if (!mounted) return;
      setState(() {
        status = '1';
      });
    }
  } catch (e) {
    if (!mounted) return;
    setState(() {
      status = '1';
    });
  }
}

PHP端修改(submit.php):

先校验请求ID是否已处理,未处理再执行插入:

<?php
$conn = mysqli_connect("localhost", "user", "pass", "db");
$requestId = $_POST['request_id'];
$userId = $_POST['id'];
$notes = $_POST['notes'];

// 校验请求ID是否已存在
$checkQuery = "SELECT id FROM submissions WHERE request_id = ?";
$stmt = mysqli_prepare($conn, $checkQuery);
mysqli_stmt_bind_param($stmt, "s", $requestId);
mysqli_stmt_execute($stmt);
mysqli_stmt_store_result($stmt);

if (mysqli_stmt_num_rows($stmt) > 0) {
    // 请求已处理,直接返回成功状态
    echo json_encode([
        'success' => '1',
        'status' => '0',
        'message' => '请求已处理'
    ]);
    mysqli_stmt_close($stmt);
    mysqli_close($conn);
    exit;
}

// 未处理则执行插入
$insertQuery = "INSERT INTO submissions (user_id, notes, request_id) VALUES (?, ?, ?)";
$stmt = mysqli_prepare($conn, $insertQuery);
mysqli_stmt_bind_param($stmt, "sss", $userId, $notes, $requestId);

if (mysqli_stmt_execute($stmt)) {
    echo json_encode([
        'success' => '1',
        'status' => '0',
        'message' => '提交成功'
    ]);
} else {
    echo json_encode([
        'success' => '0',
        'status' => '1',
        'message' => '提交失败'
    ]);
}

mysqli_stmt_close($stmt);
mysqli_close($conn);
?>

2. 超时后主动校验请求状态

客户端触发超时后,不直接提示失败,而是发起查询请求确认服务器是否已处理该请求:

Flutter端新增校验方法:

_checkRequestStatus(String requestId) async {
  const checkUrl = 'https://www.testdomain.com/app/check_status.php';
  try {
    final response = await http.post(Uri.parse(checkUrl), body: {
      "request_id": requestId
    }).timeout(const Duration(seconds: 3));

    if (response.statusCode == 200) {
      final result = json.decode(response.body);
      if (!mounted) return;
      setState(() {
        success = result['success'].toString();
        status = result['status'].toString();
      });
    } else {
      if (!mounted) return;
      setState(() {
        status = '1';
      });
    }
  } catch (e) {
    if (!mounted) return;
    setState(() {
      status = '1';
    });
  }
}

PHP端新增check_status.php:

<?php
$conn = mysqli_connect("localhost", "user", "pass", "db");
$requestId = $_POST['request_id'];

$query = "SELECT success FROM submissions WHERE request_id = ?";
$stmt = mysqli_prepare($conn, $query);
mysqli_stmt_bind_param($stmt, "s", $requestId);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt, $success);

if (mysqli_stmt_fetch($stmt)) {
    echo json_encode([
        'success' => $success,
        'status' => '0'
    ]);
} else {
    echo json_encode([
        'success' => '0',
        'status' => '1',
        'message' => '请求未找到'
    ]);
}

mysqli_stmt_close($stmt);
mysqli_close($conn);
?>

3. 服务器端优化响应及时性

确保服务器处理完请求后立刻返回响应,避免输出缓冲导致的响应延迟:
在PHP代码的响应输出后添加缓冲刷新代码:

// 输出响应内容后添加
ob_flush();
flush();

注:需确保服务器output_buffering配置允许手动刷新,部分主机可能禁用该功能。

4. 带幂等的有限重试策略

若需要重试,必须基于幂等请求,避免重复插入:

postData({int retryCount = 0}) async {
  const maxRetries = 2;
  const url = 'https://www.testdomain.com/app/submit.php';
  final requestId = const Uuid().v4();

  try {
    final response = await http.post(Uri.parse(url), body: {
      "id": user,
      "notes": notesController.text,
      "request_id": requestId
    }).timeout(const Duration(seconds: 4), onTimeout: () {
      return http.Response('Error', 408);
    });

    if (response.statusCode == 408) {
      if (retryCount < maxRetries) {
        // 重试,递增重试次数
        postData(retryCount: retryCount + 1);
      } else {
        // 重试耗尽,触发状态校验
        _checkRequestStatus(requestId);
      }
    } else {
      // 正常响应处理逻辑
      if (response.statusCode == 200) {
        final auth = json.decode(response.body);
        if (!mounted) return;
        setState(() {
          success = auth['success'].toString();
          status = auth['status'].toString();
        });
      } else {
        if (!mounted) return;
        setState(() {
          status = '1';
        });
      }
    }
  } catch (e) {
    if (retryCount < maxRetries) {
      postData(retryCount: retryCount + 1);
    } else {
      if (!mounted) return;
      setState(() {
        status = '1';
      });
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:06:25