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成功请求截图

问题根源分析
- 数据格式不匹配:Flutter代码用
json.encode发送JSON格式数据,但后端API通过$app->request->post()获取的是表单格式(x-www-form-urlencoded)的数据,后端无法解析JSON中的字段,因此判定必填字段缺失。 - 字段名不一致: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
相关产品推荐
相关产品推荐

