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

Flutter POST请求返回400错误,寻求问题排查方案

问题:Flutter注册请求返回400状态码,API提示必填字段缺失

测试本地API的POST注册请求时,404错误已解决,但当前返回400状态码。已确认跨设备连接正常,Postman可成功向数据库添加数据,但Flutter代码请求报错,错误信息显示必填字段缺失。

Flutter注册页面代码

import 'dart:convert';
import 'package:audit_finance_app/widgets/registration_widgets.dart';
import 'package:audit_finance_app/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';

import '../models/user_info.dart';

class Registration extends StatefulWidget {
  const Registration({super.key});

  @override
  State<Registration> createState() => _RegistrationState();
}

class _RegistrationState extends State<Registration> {
  final firstNameController = TextEditingController();
  final lastNameController = TextEditingController();
  final birthdayController = TextEditingController();
  final genderController = TextEditingController();
  final mobileController = TextEditingController();

  final _formKey = GlobalKey<FormState>();

  RegistrationInfo registrationInfo = RegistrationInfo();

  Future<void> registerUser(RegistrationInfo registrationInfo) async {
    final url =
        Uri.parse('http://192.168.100.228/api/v1/index.php/registration');
    // var uri = Uri.https('192.168.100.228', 'api/v1/index.php/registration');
    try {
      final response = await post(
        url,
        body: json.encode(
          registrationInfo.toJson(),
        ),
      );

      if (response.statusCode == 200) {
        print('User registered successfully!');
      } else {
        print('Failed to register user. Status code: ${response.statusCode}');
      }
    } catch (e) {}
  }

  void getTextFormFieldValue() {
    registrationInfo.first_name = firstNameController.text;
    registrationInfo.last_name = lastNameController.text;
    registrationInfo.birthday = birthdayController.text;
    registrationInfo.gender = genderController.text;
    registrationInfo.mobile = mobileController.text;

    print(registrationInfo.first_name);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('REGISTRATION'),
        centerTitle: true,
      ),
      body: Padding(
        padding: const EdgeInsets.fromLTRB(30, 15, 30, 0),
        child: Column(
          children: [
            Form(
              key: _formKey,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Widgets().sizedBoxHeight(15),
                  RegistrationWidgets().label(text: 'First Name'),
                  Widgets().sizedBoxHeight(5),
                  RegistrationWidgets().textField(
                    textEditingController: firstNameController,
                    hint: 'Please enter your first name',
                  ),
                  Widgets().sizedBoxHeight(15),
                  RegistrationWidgets().label(text: 'Last Name'),
                  Widgets().sizedBoxHeight(5),
                  RegistrationWidgets().textField(
                    textEditingController: lastNameController,
                    hint: 'Please enter your last name',
                  ),
                  Widgets().sizedBoxHeight(15),
                  RegistrationWidgets().label(text: 'Birthday'),
                  Widgets().sizedBoxHeight(5),
                  RegistrationWidgets().textField(
                    textEditingController: birthdayController,
                    hint: 'Please enter your birthday',
                  ),
                  Widgets().sizedBoxHeight(15),
                  RegistrationWidgets().label(text: 'Gender'),
                  Widgets().sizedBoxHeight(5),
                  RegistrationWidgets().textField(
                    textEditingController: genderController,
                    hint: 'Please enter your gender',
                  ),
                  Widgets().sizedBoxHeight(15),
                  RegistrationWidgets().label(text: 'Mobile Number'),
                  Widgets().sizedBoxHeight(5),
                  RegistrationWidgets().textField(
                    textEditingController: mobileController,
                    hint: 'Please enter you mobile number',
                  ),
                ],
              ),
            ),
            Widgets().sizedBoxHeight(20),
            FilledButton(
              onPressed: () {
                if (_formKey.currentState!.validate()) {
                  getTextFormFieldValue();
                  registerUser(registrationInfo);
                  Fluttertoast.showToast(
                    toastLength: Toast.LENGTH_SHORT,
                    msg: 'Registration complete',
                    gravity: ToastGravity.BOTTOM_LEFT,
                    backgroundColor: Colors.grey,
                  );
                }
              },
              style: FilledButton.styleFrom(
                minimumSize: const Size.fromHeight(50),
              ),
              child: const Text('Register'),
            ),
          ],
        ),
      ),
    );
  }
}

RegistrationInfo模型代码

class RegistrationInfo {
  String? first_name;
  String? last_name;
  String? birthday;
  String? gender;
  String? mobile;

  RegistrationInfo(
      {this.first_name,
      this.last_name,
      this.birthday,
      this.gender,
      this.mobile});

  RegistrationInfo.fromJson(Map<String, dynamic> json) {
    first_name = json['first_name'];
    last_name = json['last_name'];
    birthday = json['birthday'];
    gender = json['gender'];
    mobile = json['mobile number'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['first_name'] = first_name;
    data['last_name'] = last_name;
    data['birthday'] = birthday;
    data['gender'] = gender;
    data['mobile number'] = mobile;
    return data;
  }
}

API后端代码

$app->post('/registration', function () use  ($app){
    //Required Parameters
    verifyRequiredParams(array('first_name','last_name','birthday','gender','mobile'));
    
    //includes
    $database = new Database();
    $conn = $database->getConnection();
    
    //
    $db = new SaveInfo($conn);
    
    $response = array();
    
    $first_name = $app->request->post('first_name');
    $last_name = $app->request->post('last_name');
    $birthday = $app->request->post('birthday');
    $gender = $app->request->post('gender');
    $mobile = $app->request->post('mobile');
    

    $result = $db->savePersonalInfo($first_name,$last_name,$birthday,$gender,$mobile);
    
    if($result){
        $response['error'] = false;
        $response['message'] = "Successfully Registered";
    }
    else{
        $response['error'] = true;
        $response['message'] = "Failed to register";
    }
    echoResponse(200,$response);
});

错误信息

{"error":true,"message":"Required field(s) first_name, last_name, birthday, gender, mobile is missing or empty"}
Failed to register user. Status code: 400

Postman成功请求截图

Postman成功请求截图


问题根源分析

  1. 数据格式不匹配:Flutter代码用json.encode发送JSON格式数据,但后端API通过$app->request->post()获取的是表单格式(x-www-form-urlencoded)的数据,后端无法解析JSON中的字段,因此判定必填字段缺失。
  2. 字段名不一致:RegistrationInfo模型的toJson方法中,mobile对应的key是mobile number,但后端API期望的字段名是mobile,这会导致mobile字段无法被后端正确识别。

解决建议

1. 修改Flutter请求数据格式为表单格式

将POST请求的body改为表单键值对,而非JSON:

Future<void> registerUser(RegistrationInfo registrationInfo) async {
  final url = Uri.parse('http://192.168.100.228/api/v1/index.php/registration');
  try {
    // 构造符合后端要求的表单数据
    final requestBody = {
      'first_name': registrationInfo.first_name,
      'last_name': registrationInfo.last_name,
      'birthday': registrationInfo.birthday,
      'gender': registrationInfo.gender,
      'mobile': registrationInfo.mobile,
    };
    // 打印请求体,方便调试
    print('Request Body: $requestBody');

    final response = await post(
      url,
      body: requestBody,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
    );

    if (response.statusCode == 200) {
      print('User registered successfully!');
      print('Response: ${response.body}');
    } else {
      print('Failed to register user. Status code: ${response.statusCode}');
      print('Error Response: ${response.body}');
    }
  } catch (e) {
    print('Request Error: $e');
  }
}

2. 修正RegistrationInfo模型的字段名

确保模型的字段名与后端API一致:

class RegistrationInfo {
  String? first_name;
  String? last_name;
  String? birthday;
  String? gender;
  String? mobile;

  RegistrationInfo({
    this.first_name,
    this.last_name,
    this.birthday,
    this.gender,
    this.mobile,
  });

  RegistrationInfo.fromJson(Map<String, dynamic> json) {
    first_name = json['first_name'];
    last_name = json['last_name'];
    birthday = json['birthday'];
    gender = json['gender'];
    mobile = json['mobile']; // 修正为mobile
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['first_name'] = first_name;
    data['last_name'] = last_name;
    data['birthday'] = birthday;
    data['gender'] = gender;
    data['mobile'] = mobile; // 修正为mobile
    return data;
  }
}

3. 额外调试建议

  • 在发送请求前打印构造的请求体,确认字段名和值都正确。
  • 打印API返回的完整响应体,便于排查细节问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:15:42