11

Right, so I've been working on something which requires basic authentication through headers, and passing some variables via HTTP Post. This is a terminal app.

This is what my code looks like:

import 'package:http/http.dart' as http;
import 'dart:io';
void main() {
  var url = "http://httpbin.org/post";
  var client = new http.Client();
  var request = new http.Request('POST', Uri.parse(url));
  var body = {'content':'this is a test', 'email':'john@doe.com', 'number':'441276300056'};
  request.headers[HttpHeaders.CONTENT_TYPE] = 'application/json; charset=utf-8';
  request.headers[HttpHeaders.AUTHORIZATION] = 'Basic 021215421fbe4b0d27f:e74b71bbce';
  request.body = body;
  var future = client.send(request).then((response) => response.stream.bytesToString().then((value) => print(value.toString()))).catchError((error) => print(error.toString()));
}

I'm using httpbin as an echo server, so it tells me what I'm passing in. My code works correctly if I don't pass a body, or if I pass a string as the body.

Obviously that's because the body attribute in http.Request only accepts strings, and I'm trying to pass a map to it.

I could convert that to a string, and it would probably work, but I still think my code could be improved. Not from a syntax point of view, or from how it's handling the future, but I'm not certain using http.dart is the right thing to do.

Could someone point me in the right direction?

Thanks in advance.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Marcos Placona
  • 21,468
  • 11
  • 68
  • 93

1 Answers1

19

JSON is a String. You need to encode your map to JSON and pass it as a String.

You can use bodyFields instead of body to pass a Map.
This way your content-type is fixed to "application/x-www-form-urlencoded".

The DartDoc for post says:

/// If [body] is a Map, it's encoded as form fields using [encoding]. The
/// content-type of the request will be set to
/// `"application/x-www-form-urlencoded"`; this cannot be overridden.

I was able to send JSON data this way a while ago

return new http.Client()
  .post(url, headers: {'Content-type': 'application/json'},
      body: JSON.encoder.convert({"distinct": "users","key": "account","query": {"active":true}}))
      .then((http.Response r) => r.body)
      .whenComplete(() => print('completed'));

EDIT

import 'package:http/http.dart' as http;
import 'dart:io';
void main() {
  var url = "http://httpbin.org/post";
  var client = new http.Client();
  var request = new http.Request('POST', Uri.parse(url));
  var body = {'content':'this is a test', 'email':'john@doe.com', 'number':'441276300056'};
//  request.headers[HttpHeaders.CONTENT_TYPE] = 'application/json; charset=utf-8';
  request.headers[HttpHeaders.AUTHORIZATION] = 'Basic 021215421fbe4b0d27f:e74b71bbce';
  request.bodyFields = body;
  var future = client.send(request).then((response)
      => response.stream.bytesToString().then((value)
          => print(value.toString()))).catchError((error) => print(error.toString()));
}

produces

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "content": "this is a test", 
    "email": "john@doe.com", 
    "number": "441276300056"
  }, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Authorization": "Basic 021215421fbe4b0d27f:e74b71bbce", 
    "Connection": "close", 
    "Content-Length": "63", 
    "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", 
    "Host": "httpbin.org", 
    "User-Agent": "Dart/1.5 (dart:io)", 
    "X-Request-Id": "b108713b-d746-49de-b9c2-61823a93f629"
  }, 
  "json": null, 
  "origin": "91.118.62.43", 
  "url": "http://httpbin.org/post"
}
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thank you! Though it seems it doesn't pass the values as a form (i.e. if you make the request using the echo server I mentioned, you will see "form": {}" is impty – Marcos Placona Jun 12 '14 at 13:51
  • When you use `bodyFields` with the map or when you use `body` with `JSON.encoder.convert` and content type `'application/json'`? – Günter Zöchbauer Jun 12 '14 at 13:55
  • If you run your example against the url http://httpbin.org/post and do ".then((http.Response r) => print(r.body))" you'll see it – Marcos Placona Jun 12 '14 at 14:06
  • I understand, but which of these two version did you try? Did both fail? – Günter Zöchbauer Jun 12 '14 at 14:07
  • Fantastic! bodyfields is what I was missing! It works now. Thank you so much! – Marcos Placona Jun 12 '14 at 15:42
  • @enderland Thanks for the feedback :) – Günter Zöchbauer Apr 21 '16 at 14:29
  • how can I send a dart object via request.bodyFields ?? another object of it which make sense is **body** which expects string. I can not jsonEncode it as it contains complex data structure. Though my own toJson() method serves the data well as json if tested from postman but if I assign it to request.body its saying that it expects **Map** – Anuran Barman Sep 25 '18 at 16:31
  • 1
    There is no way to send complex data structure over the wire without serializing (jsonEncode or any other) it. – Günter Zöchbauer Sep 25 '18 at 16:34