【问题标题】:Dart HTTP POST with Map<String, dynamic> as body以 Map<String, dynamic> 为主体的 Dart HTTP POST
【发布时间】:2019-07-03 01:51:31
【问题描述】:

Dart http packagepost 方法只接受StringList&lt;int&gt;Map&lt;String, String&gt; 作为请求正文。 我需要将此类的对象作为正文与 Content-Type 标头 application/json 一起发送:

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&lt;String, dynamic&gt; 可以解决问题,但我只能发送Map&lt;String, String&gt;

请帮忙。

【问题讨论】:

    标签: json dart flutter


    【解决方案1】:

    使用String encoded = json.encode(theMap);,然后发布encoded。如果您需要特定的字符编码(例如 utf-8),则使用 utf8.encode(encoded) 进一步对字符串进行编码并发布结果字节数组。 (utf-8 不需要第二步,因为我认为这是默认设置。)

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

    • List&lt;int&gt; - 发送一个不透明的字节数组
    • String 编码 使用字符编码将字符串转换为字节 - 并发送字节 数组
    • Map&lt;String, String&gt; - 编码x-www-form-urlencoded 中的字符串键/值对并发送。

    如果您想发送更复杂的数据,则需要将其转换为上述之一(并且服务器需要知道如何对其进行解码)。这就是 content-type 标头有用的地方。最终,服务器接收一个字节数组并将其转换回,例如,一个字符串,或一些 json,或一组表单字段,或一个图像。它知道如何根据标头和任何指定的编码来执行此操作。

    【讨论】:

    • 感谢理查德的回答!你认为我应该把问题的标题改成“Dart HTTP Post with Complex Object as body”之类的吗?
    【解决方案2】:

    我在搜索如何使用List&lt;Map&lt;String, dynamic&gt;&gt; 发送 POST 请求时发现了这个问题 经过一番思考,我编写了代码来做到这一点。它也适用于初始问题(您只需将函数的参数设置为Map&lt;String, dynamic&gt; 类型)。

    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);
    }
    

    【讨论】:

      【解决方案3】:

      有一个 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);
      

      【讨论】:

        【解决方案4】:

        如果您需要将复杂的数据作为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

        【讨论】:

          【解决方案5】:

          就我而言,我遇到了 400 错误。我有这种类型的帖子正文

          Map<String, dynamic> requestBodyMap = {"user":{"email":"test@test.com","password":"123456"}};
          

          我用它编码

          String requestBody = json.encode(requestBodyMap);
          

          并在发布请求中发送此正文。问题是由于标题中的内容类型,它在标题中采用application/x-www-form-urlencoded 类型。我改成application/json,然后响应成功。 接受参数也有application/json

          【讨论】:

            猜你喜欢
            • 2020-01-10
            • 1970-01-01
            • 2021-03-25
            • 2021-03-27
            • 2021-04-13
            • 2021-09-25
            • 1970-01-01
            • 2019-08-07
            • 2018-10-21
            相关资源
            最近更新 更多