【问题标题】:How to make HTTP POST request with url encoded body in flutter?如何在颤动中使用 url 编码的主体发出 HTTP POST 请求?
【发布时间】:2018-09-22 15:59:14
【问题描述】:

我正在尝试以内容类型为 url 编码的方式发出发布请求。当我写body : json.encode(data) 时,它会编码为纯文本。

如果我写 body: data 我会收到错误 type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String' in type cast

这是数据对象

var match = {
  "homeTeam": {"team": "Team A"},
  "awayTeam": {"team": "Team B"}
};

还有我的要求

var response = await post(Uri.parse(url),
    headers: {
      "Accept": "application/json",
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: match,
    encoding: Encoding.getByName("utf-8"));

【问题讨论】:

    标签: dart flutter


    【解决方案1】:

    你可以使用 addAll()...

    var multipart = await http.MultipartFile.fromPath("userfile", file.path);//userFile must match server
    
    var requestMultipart = http.MultipartRequest("POST", Uri.parse("uri"));
    
    requestMultipart.files.add(multipart);
    
    Map<String, String> data = {
      "name_of_image": "testName",
      "id_image": "2",//if the
      "upload_type": "Insert",
      "id_location": "2"
    };
    
    requestMultipart.fields.addAll(data);
    

    【讨论】:

      【解决方案2】:

      如果您使用的是 dio,您可以使用:

      dio.post(
            '/info',
            data: {'id': 5},
            options: Options(contentType: Headers.formUrlEncodedContentType),
          );
      

      查看https://github.com/flutterchina/dio了解更多详情

      【讨论】:

        【解决方案3】:

        使用 DIO 的简单获取请求:

            Future<dynamic> get(String uri, {
            Map<String, dynamic> queryParameters,
            Options options,
          }) async {
            try {
              var response = await _dio.get(
                uri,
                queryParameters: queryParameters,
                options: options,
              );
              return response.data;
            } on SocketException catch (e) {
              throw SocketException(e.toString());
            } on FormatException catch (_) {
              throw FormatException("Unable to process the data");
            } catch (e) {
              throw e;
            }
          }
        

        使用 DIO 的简单发布请求{您也可以发送文件和图像}:

        Future<dynamic> post(
            String uri, {
            data,
            Map<String, dynamic> queryParameters,
            Options options,
          }) async {
            try {
              var response = await _dio.post(
                uri,
                data: data,
                queryParameters: queryParameters,
                options: options,
              );
              return response.data;
            } on FormatException catch (_) {
              throw FormatException("Unable to process the data");
            } catch (e) {
              throw e;
            }
          }
        

        【讨论】:

          【解决方案4】:

          我来这里只是想发出一个 HTTP POST 请求。以下是如何做到这一点的示例:

          import 'dart:convert';
          import 'package:http/http.dart';
          
          
          makePostRequest() async {
          
            final uri = Uri.parse('http://httpbin.org/post');
            final headers = {'Content-Type': 'application/json'};
            Map<String, dynamic> body = {'id': 21, 'name': 'bob'};
            String jsonBody = json.encode(body);
            final encoding = Encoding.getByName('utf-8');
          
            Response response = await post(
              uri,
              headers: headers,
              body: jsonBody,
              encoding: encoding,
            );
          
            int statusCode = response.statusCode;
            String responseBody = response.body;
          }
          

          另见

          【讨论】:

          • 如果我在标头中添加一个 Bearer 令牌,它现在可以工作了
          • @Vipin,好点子。某些 API 需要在标头中添加凭据。
          • 我的意思是,在这种情况下,API 不会发送标头。你试过吗?
          • @Vipin,我在this article的末尾讲认证。
          • 这很有帮助,尤其是因为它提示我使用 httpbin.org 可爱的服务进行 api 调试
          【解决方案5】:

          发布编码 URL 请求

           static Future<http.Response> genearalRequest() async {
            Map<String,String> headers = new Map();
            headers["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
           var data = {
              "data1": "datat1 value",
              "data2": "value2"
             };
          var parts = [];
          data.forEach((key, value) {
            parts.add('${Uri.encodeQueryComponent(key)}='
                '${Uri.encodeQueryComponent(value)}');
          });
          var formData = parts.join('&');
          
          print(formData);
          return await http.post('$baseUrl/oauth/token', headers: headers, body: formData);
          

          }

          Reference

          【讨论】:

            【解决方案6】:

            您需要添加三个额外的步骤: 首先,您需要将 JSON 映射转换为字符串(使用 json.encode) 然后,如果您想将其作为 application/x-www-form-urlencoded 发送,则需要对其进行 Uri 编码。 最后,您需要为要发布的参数命名。

            例如(注意,这里使用的是dart:io HttpClient,但基本一样):

              Future<HttpClientResponse> foo() async {
                Map<String, dynamic> jsonMap = {
                  'homeTeam': {'team': 'Team A'},
                  'awayTeam': {'team': 'Team B'},
                };
                String jsonString = json.encode(jsonMap); // encode map to json
                String paramName = 'param'; // give the post param a name
                String formBody = paramName + '=' + Uri.encodeQueryComponent(jsonString);
                List<int> bodyBytes = utf8.encode(formBody); // utf8 encode
                HttpClientRequest request =
                    await _httpClient.post(_host, _port, '/a/b/c');
                // it's polite to send the body length to the server
                request.headers.set('Content-Length', bodyBytes.length.toString());
                // todo add other headers here
                request.add(bodyBytes);
                return await request.close();
              }
            

            以上是dart:io版本(当然你可以在Flutter中使用) 如果您想坚持使用package:http 版本,那么您需要稍微调整一下您的地图。 body 必须是 Map&lt;String, String&gt;。你需要决定你想要什么作为你的 POST 参数。你想要两个:主队和客队?或者一个,比如说,teamJson?

            这段代码

              Map<String, String> body = {
                'name': 'doodle',
                'color': 'blue',
                'homeTeam': json.encode(
                  {'team': 'Team A'},
                ),
                'awayTeam': json.encode(
                  {'team': 'Team B'},
                ),
              };
            
              Response r = await post(
                url,
                body: body,
              );
            

            在电线上产生这个

            name=doodle&color=blue&homeTeam=%7B%22team%22%3A%22Team+A%22%7D&awayTeam=%7B%22team%22%3A%22Team+B%22%7D

            或者,这个

              Map<String, String> body = {
                'name': 'doodle',
                'color': 'blue',
                'teamJson': json.encode({
                  'homeTeam': {'team': 'Team A'},
                  'awayTeam': {'team': 'Team B'},
                }),
              };
            
              Response r = await post(
                url,
                body: body,
              );
            

            在电线上产生这个

            name=doodle&color=blue&teamJson=%7B%22homeTeam%22%3A%7B%22team%22%3A%22Team+A%22%7D%2C%22awayTeam%22%3A%7B%22team%22%3A% 22队+B%22%7D%7D

            package:http 客户端负责:对 Uri.encodeQueryComponent 进行编码、utf8 编码(注意这是默认值,因此无需指定)并在 Content-Length 标头中发送长度。您仍然必须进行 json 编码。

            【讨论】:

            • 如果您不关心特定端口,httpClient.postUrl 也可以!
            • 感谢您的回答。但似乎此解决方案仅适用于未嵌入其他对象的对象。因此,如果我删除 homeTeam 对象和 awayTeam 对象,它可以正常工作。
            • 根据文档 - [body] 设置请求的正文。它可以是 [String]、[List] 或 [Map]。如果是字符串,则使用 [encoding] 对其进行编码并用作请求的正文。请求的内容类型将默认为“text/plain”。
            • 所以如果我想发送复杂的结构,我必须将内容类型设置为application/json。 url-encoded 仅适用于单级对象。
            • 如何使用 HttpClientRequest 传递多个参数?
            【解决方案7】:

            在“UserModel”的地方写上你的模型的名字,不要在正文中传递字符串,它会使用下面给出的“Map”产生问题。

            static Future<UserModel> performUserLogin(String username, String password) async{
                
                    try{
                
                      Map<String, String> headers = {"Content-type": "application/json"};
                      
                
                      Map<String, String> JsonBody = {
                        'username':  username,
                        'password': password
                      };
                
                      print("The JSON Object is here $body");
                      // make POST request
                      final response = await http.post(loginAPIURL,body: JsonBody);
                      // check the status code for the result
                      int statusCode = response.statusCode;
                      print("Login calling $response");
                
                
                      if (statusCode == 200){
                
                      
                
                      }else{
                        return null;
                        //return UserModel();
                        throw Exception("Error in login");
                      }
                
                    } catch (e) {
                      throw Exception(e);
                    }
                  }
            

            【讨论】:

              【解决方案8】:
              Map<String, String> body = {
                'getDetails': 'true'
              };
              
              final response = await 
              http.post("https://example.com/", body: body);
              
              if (response.statusCode == 200) {
              //Your code
              }
              

              【讨论】:

                【解决方案9】:

                我建议使用Dio 库。它支持很多使用 API。

                使用最新的 Dio 版本。只需执行以下操作:

                BaseOptions options = new BaseOptions(
                baseUrl: "https://www.xx.com/api",
                connectTimeout: 5000,
                receiveTimeout: 3000,);
                Dio dio = new Dio(options);
                //
                Map<String, String> params = Map();
                params['username'] = '6388';
                params['password'] = '123456';
                //
                response = await dio.post("/login", data: FormData.fromMap(params));
                

                【讨论】:

                  【解决方案10】:

                  我是用 dart 的 http 包做的。这不是太花哨。我的端点不接受其他方法的参数,但它像这样接受了它们,参数中包含括号。

                  import 'package:http/http.dart' as http;
                  
                  String url = "<URL>";
                  
                  Map<String, String> match = {
                    "homeTeam[team]": "Team A",
                    "awayTeam[team]": "Team B",
                  };
                  
                  Map<String, String> headers = {
                      "Content-Type": "application/x-www-form-urlencoded"
                  }
                  
                  http.post(url, body: body, headers: headers).then((response){
                      print(response.toString());
                  });
                  

                  【讨论】:

                    【解决方案11】:

                    你需要使用 json.encode

                    例子;

                    var match = {
                      "homeTeam": {"team": "Team A"},
                      "awayTeam": {"team": "Team B"}
                    };
                    var response = await post(Uri.parse(url),
                        headers: {
                          "Accept": "application/json",
                          "Content-Type": "application/x-www-form-urlencoded"
                        },
                        body: json.encode(match),
                        encoding: Encoding.getByName("utf-8"));
                    

                    【讨论】:

                    • 那个编码部分对我很有帮助。
                    • 在 http/http.dart 中,如果我们使用 json.encode 对 body 进行编码,它将不起作用。只需将 body 作为 map 传递,它将使用 encoding 编码为表单字段。
                    • 这就是为我完成示例的内容。我制作了一张地图,但将其作为参数传递给 http.post(在正文中)并没有成功。对其进行编码,我的 PHP rest api 可以完美读取。
                    【解决方案12】:

                    向大家推荐dio包,dio是Dart/Flutter强大的Http客户端,支持拦截器、FormData、请求取消、文件下载、超时等。

                    dio 非常易于使用,您可以:

                    Map<String, String> body = {
                    'name': 'doodle',
                    'color': 'blue',
                    'teamJson': {
                      'homeTeam': {'team': 'Team A'},
                      'awayTeam': {'team': 'Team B'},
                      },
                    };
                    
                    dio.post("/info",data:body, options: 
                      new Options(contentType:ContentType.parse("application/x-www-form-urlencoded")))    
                    

                    dio可以自动编码数据。

                    更多详情请参考dio

                    【讨论】:

                    • 看起来很有希望。 @wendu 使用这个模块有什么好处?
                    • 如果您隶属于该包,请在帖子中披露您的隶属关系。
                    • 这甚至不会编译,它应该是 &lt;String, dynamic&gt; 的 Map 而不是 &lt;String, String&gt;
                    猜你喜欢
                    • 2022-11-03
                    • 1970-01-01
                    • 2022-01-05
                    • 2018-12-24
                    • 2020-06-22
                    • 1970-01-01
                    • 2017-11-28
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多