Reputation: 3506
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
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
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
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
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
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 arrayString
encodes the
string into bytes using a character encoding - and sends the byte
arrayMap<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