使用Map<String, dynamic>作为请求体的Dart HTTP POST请求

4
Dart的http package中的post方法仅接受StringList<int>Map<String, String>作为请求body。我需要发送一个该类对象作为带有Content-Type头部application/json的body。
class CreateListingRequest {
  String title;
  List<ListingImage> images;
  List<int> categoryIds;
}

其中ListingImage

class ListingImage {
  String url;
  int position;
}

在Postman中,我会使用Content-Type头部为application/json的原始json构建请求体,如下所示:
{
  "title": "Testing transaction force fail",
  "listing_images": [
    {
      "url": "https://picsum.photos/500/500/?image=336",
      "position": 0
    },
    {
      "url": "https://picsum.photos/500/500/?image=68",
      "position": 1
    },
    {
      "url": "https://picsum.photos/500/500/?image=175",
      "position": 2
    }
  ],
  "category_ids": [19, 26]
}

在我看来,如果我能发送一个Map<String, dynamic>,那么问题就可以解决了,但我只能发送Map<String, String>

请帮忙。

5个回答

10

使用 String encoded = json.encode(theMap); 方法将数据转换为字符串后发送 encoded。如果需要特定的字符编码(例如 utf-8),则需要进一步使用 utf8.encode(encoded) 方法将字符串进行编码并发送结果字节数组。(对于 utf-8,第二步应该是不必要的,因为它是默认值。)

值得考虑的是这 3 种变体的作用:

  • List<int> - 发送一个不透明的字节数组
  • String - 使用字符编码将字符串编码为字节数组并发送
  • Map<String, String> - 将字符串键/值对编码为 x-www-form-urlencoded 格式并发送。

如果要发送更复杂的数据,则需要将其转换为上述之一的数据类型(并且服务器需要知道如何解码它)。这就是 content-type 标头有用的地方。最终,服务器接收到字节数组,并将其转换回字符串、JSON、一组表单字段或图像等。它可以根据标头和任何指定的编码方式进行转换。


谢谢你的回答,Richard!你觉得我应该把问题的标题改成类似于“使用复杂对象作为请求体的Dart HTTP Post”吗? - Touré Holder

8

在搜索如何使用List<Map<String, dynamic>>发送POST请求时,我发现了这个问题。 经过一些思考,我编写了代码来完成此操作。它也适用于初始问题(只需将函数的参数设置为Map<String, dynamic>类型即可)。

import 'package:http/http.dart' as http;
import 'dart:convert';

//...

static const Map<String, String> _JSON_HEADERS = {
    "content-type": "application/json"
  };

void sendPost(List<Map<String, dynamic>> data) {
  http.Client client = new http.Client();
  final String encodedData = json.encode(data);
  client.post(ADDRESS, //your address here
              body: encodedData, headers: _JSON_HEADERS);
}

1
在我的情况下,我遇到了400错误。我有一个类型为的帖子正文。
Map<String, dynamic> requestBodyMap = {"user":{"email":"test@test.com","password":"123456"}};

我使用编码对其进行了编码

String requestBody = json.encode(requestBodyMap);

并在post请求中发送了此正文。问题是由于头部中的内容类型,它正在使用application/x-www-form-urlencoded类型。我将其更改为application/json类型,然后响应成功了。 接受参数也是application/json


1
如果您需要将复杂数据作为POST请求的主体发布,即将其作为对象本身而不是JSON编码的字符串发布,服务器必须在其端再次解码它,您可以使用Flutter包Dio代替http。我刚刚发布了这个。
import 'package:dio/dio.dart';

                  Response res;
                  Dio dio = new Dio();
                  try {
                    res = await dio.post('<url>', data: {
                      "Body": {
                        "stkCallback": {
                          "MerchantRequestID": "21605-295434-4",
                          "CheckoutRequestID": "ws_CO_04112017184930742",
                          "ResultCode": '0',
                          "ResultDesc": "The service request is processed successfully.",
                          "CallbackMetadata": {
                            "Item": [
                              {"Name": "Amount", "Value": 1},
                              {"Name": "MpesaReceiptNumber", "Value": "LK451H35OP"},
                              {"Name": "Balance"},
                              {"Name": "TransactionDate", "Value": 20171104184944},
                              {"Name": "PhoneNumber", "Value": 254727894083}
                            ]
                          }
                        }
                      }
                    });
                  } catch (e) {
                    print('Caught an error in API call!');
                    print('e is: ${e.toString()}');
                    Alert(context: context, title: 'Http POST error', desc: '$e').show();
                    if (res != null) print('Status code in apiCall() catch is ${res.statusCode}');
                  }

这是 Dio 包:https://pub.dev/packages/dio

0

有一个Dart包提供了一些帮助类来处理HTTP请求,还有一个帮助进行JSON序列化的包。

BasicUtils: https://github.com/Ephenodrom/Dart-Basic-Utils

JsonSerializable: https://flutter.dev/docs/development/data-and-backend/json

安装方法:

dependencies:
  basic_utils: ^1.4.0
  json_annotation: ^2.0.0

dev_dependencies:
  test: '>=0.12.42 <2.0.0'
  build_runner: ^1.0.0
  json_serializable: ^2.0.0

用法

按照以下方式更新您的类:

import 'package:json_annotation/json_annotation.dart';

part 'CreateListingRequest.g.dart';

@JsonSerializable()
class CreateListingRequest {
  String title;
  List<ListingImage> images;
  List<int> categoryIds;

  factory CreateListingRequest.fromJson(Map<String, dynamic> json) =>
      _$CreateListingRequestFromJson(json);

  Map<String, dynamic> toJson() => _$CreateListingRequestToJson(this);
}

import 'package:json_annotation/json_annotation.dart';

part 'ListingImage.g.dart';

@JsonSerializable()
class ListingImage {
  String url;
  int position;

  factory ListingImage.fromJson(Map<String, dynamic> json) =>
      _$ListingImageFromJson(json);

  Map<String, dynamic> toJson() => _$ListingImageToJson(this);
}

使用以下命令创建 *.g.dart 文件:

pub run build_runner build

现在你的工作区中有一些*.g.dart文件。之后,您可以使用这些类来进行http请求。

//Convert class to string
String body = json.encode(payload.toJson());

// Convert response to class
MyResponseClass responseAsClass = MyResponseClass.fromJson(responseData);

完整示例

// Define some headers and query parameters
Map<String, String> headers = {
  "Accept": "application/json"
};
Map<String, String> queryParameters = {
  "foo": "bar"
};

// Create instance of the class
CreateListingRequest payload = CreateListingRequest();

// Convert class to String
String body = json.encode(payload.toJson()); // that does the magic :)

// Send request
Map<String, dynamic> responseData = await HttpUtils.postForJson("api.com/dosomething", body,
      headers: headers, queryParameters: queryParameters);

// Convert response to class
MyResponseClass responseAsClass = MyResponseClass.fromJson(responseData);

附加信息:

这些都是来自 HttpUtils 类的方法。

Future<Map<Response> getForFullResponse(String url,{Map<String, dynamic> queryParameters,Map<String, String> headers});
Future<Map<String, dynamic>> getForJson(String url,{Map<String, dynamic> queryParameters,Map<String, String> headers});
Future<String> getForString(String url,{Map<String, dynamic> queryParameters,Map<String, String> headers});
Future<Map<Response> postForFullResponse(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<Map<String, dynamic>> postForJson(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<String> postForString(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<Response> putForFullResponse(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<Map<String, dynamic>> putForJson(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<String> putForString(String url, String body,{Map<String, String> queryParameters,Map<String, String> headers});
Future<Response deleteForFullResponse(String url,{Map<String, String> queryParameters,Map<String, String> headers});
Future<Map<String, dynamic>> deleteForJson(String url,{Map<String, String> queryParameters,Map<String, String> headers});
Future<String> deleteForString(String url,{Map<String, String> queryParameters,Map<String, String> headers});
Map<String, dynamic> getQueryParameterFromUrl(String url);
String addQueryParameterToUrl(String url, Map<String, dynamic> queryParameters);

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