Flutter HTTP Post 请求未在 Raspberry Pi 上到达 Node-RED
Flutter HTTP Post Request does not reach Node-RED on Raspberry Pi
我正在尝试通过 HTTP(而非 HTTPS)将数据从 Flutter 发送到 Node-RED(到 Raspberry Pi)。 URL http://bierbrauserver.ddns.net 来自 NO-IP (DDNS)。从 Youtube 教程传输到测试服务器工作正常。但是当我将 URL 从“https://reqres.in/api/users”更改为“http://bierbrauserver.ddns.net:1880”时,我在调试 window 中没有得到任何输入节点红色。我尝试在 AndroidManifest.xml 中添加条目
所以允许http,但这也没有解决问题。
此外,我检查了 Raspberry Pi 所在网络的 WLAN 路由器配置中的端口转发。这里配置了 HTTP 和 1880 (Node-RED) 的端口转发。 IP 地址的连接应该没问题,因为我可以从外部端口访问数据库,该端口也位于 Raspberry Pi。难道只能httpS post 请求吗?
Future<UserModel> createUser(String name, String jobTitle)async{
const String Url = "https://reqres.in/api/users";
// const String Url = "https://bierbrauserver.ddns.net:1880";
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final String responseString = response.body;
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
感谢您的帮助。
将您的代码更改为:
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final responseString = jsonDecode(_res.body)
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
问题已解决。程序代码已经被修改,因为我忘记了在问题修复时立即回答问题。问题隐藏在 Node-RED 程序中。在 Node-RED 中选择了错误的节点。
URL 用“/test”扩展,在“http in”节点的 Node-RED 中重复使用。现在一切正常。
供您参考:“Zutat_1”、“Zutat_2”、“Zutat_3”和“Zutat_4”是该程序中的全局变量,因此不必须传递给“createRezept”函数。
此外,应该提到的是,代码中一些不属于英语的元素是用德语执行的。
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
这是用于解析的代码。(这是在一个单独的文件中,在我的例子中名为“Rezept_model.dart”)
import 'dart:convert';
RezeptModel rezeptModelFromJson(String str) => RezeptModel.fromJson(json.decode(str));
String rezeptModelToJson(RezeptModel data) => json.encode(data.toJson());
class RezeptModel {
RezeptModel({
required this.zutat1,
required this.zutat2,
required this.zutat3,
required this.zutat4,
required this.createdAt,
});
String zutat1;
String zutat2;
String zutat3;
String zutat4;
DateTime createdAt;
factory RezeptModel.fromJson(Map<String, dynamic> json) => RezeptModel(
zutat1: json["zutat1"],
zutat2: json["zutat2"],
zutat3: json["zutat3"],
zutat4: json["zutat4"],
createdAt: DateTime.parse(json["createdAt"]),
);
Map<String, dynamic> toJson() => {
"zutat1": zutat1,
"zutat2": zutat2,
"zutat3": zutat3,
"zutat4": zutat4,
"createdAt": createdAt.toIso8601String(),
};
}
为了完整起见,这里是主程序的全部代码。
import 'dart:convert';
import 'dart:math';
import 'package:bier_brau_project/variables.dart';
import 'package:bier_brau_project/rezept_model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bier Brau App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'HTTP Test Site'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
class _MyHomePageState extends State<MyHomePage> {
RezeptModel _rezept = RezeptModel(zutat1: "init", zutat2: "init", zutat3: "init",zutat4: "init", createdAt: DateTime.now());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MySlider(Min:22.0,Max:40.0,Devisions: 100,unit:'l',title: 'Wasser',ID:1),
MySlider(Min:4.0,Max:20.0,Devisions: 100,unit:'kg',title:'Malz',ID: 2,),
MySlider(Min:60.0,Max:300.0,Devisions: 100,unit:'g',title:'Hopfen',ID: 3,),
MySlider(Min:12.0,Max:60.0,Devisions: 100,unit:'g',title:'Hefe',ID:4),
const SizedBox(height:32.0,),
Text( "Wasser "+ _rezept.zutat1 +"\n"
+"Malz "+_rezept.zutat2 +"\n"
+"Mopfen "+ _rezept.zutat3 +"\n"
+"Hefe "+ _rezept.zutat4 +"\n"
+"ist Erfolgreich übertragen worden "
+ _rezept.createdAt.toIso8601String())
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()async{
final RezeptModel rezept= await createRezept();
setState(() {
_rezept=rezept;
});
},
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
/// This is the stateful widget that the main application instantiates.
class MySlider extends StatefulWidget {
MySlider({Key? key,required this.Min, required this.Max,required this.Devisions,required this.unit,required this.title,required this.ID}): super(key: key);
final double Min;
final double Max;
final String unit;
final String title;
final int Devisions;
final int ID;
bool ValueChanged= false;
@override
State<MySlider> createState() => _MySliderState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MySliderState extends State<MySlider> {
double _currentSliderValue =0;
@override
Widget build(BuildContext context) {
return Padding (
padding: const EdgeInsetsDirectional.fromSTEB(32.0, 32.0, 32.0, 16.0),
child: Column(children: <Widget> [
Text(
widget.title,
style: const TextStyle(
color: Colors.black54,
height: 1,
fontSize: 18
),
),
Slider(
value: widget.ValueChanged== false ? ((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue,
min: widget.Min,
max: widget.Max,
divisions: widget.Devisions,
label: _currentSliderValue.toStringAsFixed(2),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
first_call = false;
widget.ValueChanged =true;
switch(widget.ID) {
case 1: {Zutat_1 =double.parse(value.toStringAsFixed(2));}
break;
case 2: {Zutat_2 =double.parse(value.toStringAsFixed(2));}
break;
case 3: {Zutat_3 =double.parse(value.toStringAsFixed(2));}
break;
case 4: {Zutat_4 =double.parse(value.toStringAsFixed(2));}
break;
default: {}
break;
}
});
},
),
Text(
(widget.ValueChanged== false ?((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue.toStringAsFixed(2)).toString() + ' '+widget.unit,
),
])
);
}
}
非常感谢所有处理过这个问题的人。
这里仍然有两张图片存放在连接处以澄清。
Node-RED program to receive from http post
Android Studio emulator with the test application
我正在尝试通过 HTTP(而非 HTTPS)将数据从 Flutter 发送到 Node-RED(到 Raspberry Pi)。 URL http://bierbrauserver.ddns.net 来自 NO-IP (DDNS)。从 Youtube 教程传输到测试服务器工作正常。但是当我将 URL 从“https://reqres.in/api/users”更改为“http://bierbrauserver.ddns.net:1880”时,我在调试 window 中没有得到任何输入节点红色。我尝试在 AndroidManifest.xml 中添加条目 所以允许http,但这也没有解决问题。 此外,我检查了 Raspberry Pi 所在网络的 WLAN 路由器配置中的端口转发。这里配置了 HTTP 和 1880 (Node-RED) 的端口转发。 IP 地址的连接应该没问题,因为我可以从外部端口访问数据库,该端口也位于 Raspberry Pi。难道只能httpS post 请求吗?
Future<UserModel> createUser(String name, String jobTitle)async{
const String Url = "https://reqres.in/api/users";
// const String Url = "https://bierbrauserver.ddns.net:1880";
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final String responseString = response.body;
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
感谢您的帮助。
将您的代码更改为:
final response = await http.post(Uri.parse(Url),body:
{
"name": name,
"job": jobTitle
});
if (response.statusCode == 201) {
final responseString = jsonDecode(_res.body)
return userModelFromJson(responseString);
}
else
{
print(response.statusCode);
return UserModel(name:'Fail',job: 'Fail', id: 'Fail', createdAt: DateTime.now());
}
}
问题已解决。程序代码已经被修改,因为我忘记了在问题修复时立即回答问题。问题隐藏在 Node-RED 程序中。在 Node-RED 中选择了错误的节点。 URL 用“/test”扩展,在“http in”节点的 Node-RED 中重复使用。现在一切正常。
供您参考:“Zutat_1”、“Zutat_2”、“Zutat_3”和“Zutat_4”是该程序中的全局变量,因此不必须传递给“createRezept”函数。 此外,应该提到的是,代码中一些不属于英语的元素是用德语执行的。
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
这是用于解析的代码。(这是在一个单独的文件中,在我的例子中名为“Rezept_model.dart”)
import 'dart:convert';
RezeptModel rezeptModelFromJson(String str) => RezeptModel.fromJson(json.decode(str));
String rezeptModelToJson(RezeptModel data) => json.encode(data.toJson());
class RezeptModel {
RezeptModel({
required this.zutat1,
required this.zutat2,
required this.zutat3,
required this.zutat4,
required this.createdAt,
});
String zutat1;
String zutat2;
String zutat3;
String zutat4;
DateTime createdAt;
factory RezeptModel.fromJson(Map<String, dynamic> json) => RezeptModel(
zutat1: json["zutat1"],
zutat2: json["zutat2"],
zutat3: json["zutat3"],
zutat4: json["zutat4"],
createdAt: DateTime.parse(json["createdAt"]),
);
Map<String, dynamic> toJson() => {
"zutat1": zutat1,
"zutat2": zutat2,
"zutat3": zutat3,
"zutat4": zutat4,
"createdAt": createdAt.toIso8601String(),
};
}
为了完整起见,这里是主程序的全部代码。
import 'dart:convert';
import 'dart:math';
import 'package:bier_brau_project/variables.dart';
import 'package:bier_brau_project/rezept_model.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bier Brau App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'HTTP Test Site'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
Future<RezeptModel> createRezept()async{
const String Url = "http://bierbrauserver.ddns.net:1880/test";
final response = await http.post(Uri.parse(Url),body:
{
"zutat1": Zutat_1.toStringAsFixed(2),
"zutat2": Zutat_2.toStringAsFixed(2),
"zutat3": Zutat_3.toStringAsFixed(2),
"zutat4": Zutat_4.toStringAsFixed(2),
"createdAt" : DateTime.now().toIso8601String()
});
if (response.statusCode == 201) {
final String responseString = response.body;
return rezeptModelFromJson(responseString);
}
else
{
print(response.statusCode);
return RezeptModel(zutat1:"Fehler",zutat2: "Fehler", zutat3: "Fehler", zutat4: "Fehler",createdAt: DateTime.now());
}
}
class _MyHomePageState extends State<MyHomePage> {
RezeptModel _rezept = RezeptModel(zutat1: "init", zutat2: "init", zutat3: "init",zutat4: "init", createdAt: DateTime.now());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MySlider(Min:22.0,Max:40.0,Devisions: 100,unit:'l',title: 'Wasser',ID:1),
MySlider(Min:4.0,Max:20.0,Devisions: 100,unit:'kg',title:'Malz',ID: 2,),
MySlider(Min:60.0,Max:300.0,Devisions: 100,unit:'g',title:'Hopfen',ID: 3,),
MySlider(Min:12.0,Max:60.0,Devisions: 100,unit:'g',title:'Hefe',ID:4),
const SizedBox(height:32.0,),
Text( "Wasser "+ _rezept.zutat1 +"\n"
+"Malz "+_rezept.zutat2 +"\n"
+"Mopfen "+ _rezept.zutat3 +"\n"
+"Hefe "+ _rezept.zutat4 +"\n"
+"ist Erfolgreich übertragen worden "
+ _rezept.createdAt.toIso8601String())
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: ()async{
final RezeptModel rezept= await createRezept();
setState(() {
_rezept=rezept;
});
},
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
/// This is the stateful widget that the main application instantiates.
class MySlider extends StatefulWidget {
MySlider({Key? key,required this.Min, required this.Max,required this.Devisions,required this.unit,required this.title,required this.ID}): super(key: key);
final double Min;
final double Max;
final String unit;
final String title;
final int Devisions;
final int ID;
bool ValueChanged= false;
@override
State<MySlider> createState() => _MySliderState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MySliderState extends State<MySlider> {
double _currentSliderValue =0;
@override
Widget build(BuildContext context) {
return Padding (
padding: const EdgeInsetsDirectional.fromSTEB(32.0, 32.0, 32.0, 16.0),
child: Column(children: <Widget> [
Text(
widget.title,
style: const TextStyle(
color: Colors.black54,
height: 1,
fontSize: 18
),
),
Slider(
value: widget.ValueChanged== false ? ((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue,
min: widget.Min,
max: widget.Max,
divisions: widget.Devisions,
label: _currentSliderValue.toStringAsFixed(2),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
first_call = false;
widget.ValueChanged =true;
switch(widget.ID) {
case 1: {Zutat_1 =double.parse(value.toStringAsFixed(2));}
break;
case 2: {Zutat_2 =double.parse(value.toStringAsFixed(2));}
break;
case 3: {Zutat_3 =double.parse(value.toStringAsFixed(2));}
break;
case 4: {Zutat_4 =double.parse(value.toStringAsFixed(2));}
break;
default: {}
break;
}
});
},
),
Text(
(widget.ValueChanged== false ?((widget.Max-widget.Min)/2)+widget.Min : _currentSliderValue.toStringAsFixed(2)).toString() + ' '+widget.unit,
),
])
);
}
}
非常感谢所有处理过这个问题的人。 这里仍然有两张图片存放在连接处以澄清。
Node-RED program to receive from http post
Android Studio emulator with the test application