Touré Holder
Touré Holder

Reputation: 3506

Dart HTTP POST with Map<String, dynamic> as body

The Dart http package's post method only accepts a String, a List<int> or a Map<String, String> as the request body. I need to send and object of this class as body with Content-Type header application/json:

class CreateListingRequest {
  String title;
  List<ListingImage> images;
  List<int> categoryIds;
}

where ListingImage is

class ListingImage {
  String url;
  int position;
}

In Postman I would build the body as raw json with Content-Type header application/json like this:

{
  "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]
}

It seems to me that if I could send a Map<String, dynamic> that would solve the problem but I can only send Map<String, String>.

Help please.

Upvotes: 4

Views: 18364

Answers (5)

dushyant trivedi
dushyant trivedi

Reputation: 11

In my case I was getting a 400 error. I had the post body of the type

Map<String, dynamic> requestBodyMap = {"user":{"email":"[email protected]","password":"123456"}};

I encoded it using

String requestBody = json.encode(requestBodyMap);

and sent this body in the post request. The problem was due to the content type in header, it was taking application/x-www-form-urlencoded type in header. I changed it to type application/json and then the response was successful. Accept parameter also had application/json.

Upvotes: 1

Karolina Hageg&#229;rd
Karolina Hageg&#229;rd

Reputation: 1387

If you need to post complicated data as the body of a POST request, that is, post it as the object itself and NOT as a JSON-encoded string that the server must decode again in its end, you can use the Flutter package Dio instead of http. I just posted this:

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

Here's the Dio package: https://pub.dev/packages/dio

Upvotes: 1

Valentina Konyukhova
Valentina Konyukhova

Reputation: 5324

I found this question while searching how to send a POST request with List<Map<String, dynamic>> After some thoughts, I write the code to do this. It will work also for the initial question (you just need to set parameter of the function to be of Map<String, dynamic> type).

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

Upvotes: 8

Ephenodrom
Ephenodrom

Reputation: 1893

There is a dart package that provides some helper classes for http requests and a package that helps with json serializing.

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

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

Install it with:

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

Usage

Update your classes like this :

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

Create the *.g.dart files with :

pub run build_runner build

Now you have some *.g.dart files in your workspace. After that you can use your classes for your http requests.

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

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

Full example

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

Additional information :

These are all methods from the HttpUtils class.

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

Upvotes: 0

Richard Heap
Richard Heap

Reputation: 51692

Use String encoded = json.encode(theMap); then post encoded. If you need a particular character encoding (e.g. utf-8) then further encode the string using utf8.encode(encoded) and post the resulting byte array. (The second step should be unnecessary for utf-8 as I think that is the default.)

It's worth considering what the 3 variants do:

  • List<int> - sends an opaque byte array
  • String encodes the string into bytes using a character encoding - and sends the byte array
  • Map<String, String> - encodes the string key/value pairs in x-www-form-urlencoded and sends that.

If you want to send more complex data then you need to convert it into one of the above (and the server needs to know how to decode it). That's where the content-type header is useful. Ultimately, the server receives a byte array and converts it back into, for example, a string, or some json, or a set of form fields, or an image. It knows how to do this based on the header and any specified encoding.

Upvotes: 10

Related Questions