如何在Flutter中将嵌套的Json作为Http POST请求的正文发送

3

我正在尝试从我一直在开发的Flutter应用程序中发送一个嵌套的Json作为HTTP POST请求的body。

{
      "user" : {
        "UserName": "username",
        "password":"password",
        "Name": "name",
        "Email": "email"
      }
}

我尝试了许多在线方法来做这件事,但每次都会收到500错误。以下是一个将其转换为Json的类。

class SignupJson {
  String username;
  String email;
  String name;
  String password;

  SignupJson(this.email, this.name, this.password, this.username);

  Map toJson() =>{"user":{
    'UserName': username,
    'Name': name,
    'password': password,
    'Email': email
  }};

}

并将其传递给此处以进行POST请求。(我放置了一个随意的URL链接)

Future<int> attemptSignup ({String username, String password, String name, String email}) async {



    SignupJson data = SignupJson(username: username, password: password, name: name, email: email);
    var url = 'url';

    String body = jsonEncode(json);

    var res = await http.post(url,
    body: body);
    return res.statusCode;

  }
5个回答

1
添加如下头信息:

Map<String, String> headers = {HttpHeaders.contentTypeHeader: "application/json"};

在POST请求中:

var res = await http.post(url, headers: headers, body: body);

1
嗨!事实上,后端不期望在此POST请求中包含标题。这仍然是一个明智的选择吗? - Siddharth Singh

0

由于这个问题还没有得到答复,我来试着解答一下。我也花了和其他人一样的时间来排除这个问题,真的非常令人沮丧。

我学到的是,内容类型头的作用是改变Client.post创建请求时如何构建主体内容的方式,同时通知服务器期望接收哪种类型的数据。在我的情况下,我错误地将它设置为'application/x-www-form-urlencoded',尽管这对于我传递单层对象和类的单个地图到其他路线上是有效的,但是对于任何连接有附带对象的键的操作都失败了。

如果我尝试通过键传递一个地图,它会失败:

Expected a value of type 'String', but got one of type 'IdentityMap<String, dynamic>'.

当我正确设置了头部但传递了一个 map 而不是 json.encode 后的 map 时,我得到了以下错误:

Bad state: Cannot set the body fields of a Request with content-type "application/json".

从Dart文档中了解到post函数的相关信息:

发送一个带有给定头和主体的HTTP POST请求到给定的URL。

body设置请求的主体。它可以是字符串、列表或Map<String, String>。如果是字符串,则使用编码进行编码,并用作请求的主体。请求的内容类型将默认为"text/plain"。

如果body是一个列表,则将其用作请求主体的字节列表。

如果body是一个Map,则使用编码将其编码为表单字段。请求的内容类型将被设置为"application/x-www-form-urlencoded";这不能被覆盖。

因此,我们可以看到,如果缺少特定的内容类型标头,或者如果将内容类型设置为application/json并将map传递给body,则该map将导致post函数覆盖内容类型标头并将标头设置为urlencoded。

因此,即使服务器不期望内容类型标头,它也期望JSON编码的数据,而实现这一点的唯一方法是同时执行以下操作:

  • 将 content-type 头设置为 application/json AND
  • 使用 jsonEncode(map) 或 json.encode(map)(两者相同)将正确的 json 编码映射作为 body 参数传递,而不是普通映射。

0

我根据这个JSON编写代码:

{
"user": {
    "UserName": "username",
    "password": "password",
    "Name": "name",
    "Email": "email"
  }
 }

你的Post Api调用可以像这样:

 Future<User> postUser(String username, String password, String name, String 
 email) async {

 Paste your api url here
 String url = '';
   final response = await http.post(apiUrl, headers: {
   // Enter your headers parameter if needed
   //  E.g: 
   'Authorization' : 'xyz',
   'Content-Type' : 'application/json'
 },
 body: jsonEncode(<String, String>{
   'UserName' : username,
   'password' : password,
   'Name' : name,
   'Email' : email
  }));

 if (response.statusCode == 200) {
    var data = jsonDecode(response.body.toString());
    print(data);
    return User.fromJson(jsonDecode(response.body));
  } else {
   throw Exception('Failed to post user.');
  }
}

你的模型应该像这样:

  class User {
     User? user;

     User({this.user});

     User.fromJson(Map<String, dynamic> json) {
       user = json['user'] != null ? new User.fromJson(json['user']) : null;
     }

    Map<String, dynamic> toJson() {
      final Map<String, dynamic> data = new Map<String, dynamic>();
      if (this.user != null) {
        data['user'] = this.user!.toJson();
      }
      return data;
     }
   }

  class User {
    String? userName;
    String? password;
    String? name;
    String? email;

      User({this.userName, this.password, this.name, this.email});

      User.fromJson(Map<String, dynamic> json) {
       userName = json['UserName'];
       password = json['password'];
       name = json['Name'];
       email = json['Email'];
     }

    Map<String, dynamic> toJson() {
       final Map<String, dynamic> data = new Map<String, dynamic>();
        data['UserName'] = this.userName;
        data['password'] = this.password;
        data['Name'] = this.name;
       data['Email'] = this.email;
      return data;
     }
    }

0

将 map 定义为 <String, dynamic>,例如:

Map<String, dynamic> data = {
"User": {
          "UserName":"username",
          "Password":"password"
    }
};

并将以下内容添加到标头中:

HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.postUrl(Uri.parse(url));
request.headers.set('Accept', 'application/json');
request.headers.set('Content-type', 'application/json');
request.add(utf8.encode(json.encode(data)));
HttpClientResponse response = await request.close();
String reply = await utf8.decoder.bind(response).join();
httpClient.close();

即使后端不期望此请求带有标头,该怎么办?在我的逻辑流程中,此请求应输出statusCode(如果等于200,则将人们带到下一个屏幕)。那么在这里获取状态码的位置在哪里呢?我对reply有点困惑。 - Siddharth Singh
你正在向后端发送 JSON,因此必须设置 Content-type。response 具有名为 statusCode 的属性,您可以在其中检查它是否为 200。因此,您可以像这样检查:if (response.statusCode == 200) - Sukhi
有没有办法我可以联系你?你的个人资料上说你接受客户。 - Siddharth Singh

0

我在这个问题上卡了两天了。以下是我克服问题的方法:

Future<int> attemptSignup ({String username, String password, String name, String email}) async {


SignupJson data = SignupJson(username: username, password: password, name: name, email: email);
var url = 'url';

String body = jsonEncode(data);     

//here jsonEncode(data) return String bt in http body you are passing Map value

//So you have to convert String to Map
Map bodyMap = jsonDecode(body);         

// your nested json data
var bodyData = {       // where var store <String, dynamic> data as your demand
      "user" : bodyMap  
};


var res = await http.post(url,
body: bodyData,
headers: {"Content-Type": "application/json",},
);

return res.statusCode;

}

确保在HTTP中添加标题。

@Siddharth Singh,请检查一下这个,让我知道它是否对你有效 :) - Mimu Saha Tishan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接