92

This is my code to make a request to an API:

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

Future<http.Response> postRequest () async {
  var url ='https://pae.ipportalegre.pt/testes2/wsjson/api/app/ws-authenticate';
  var body = jsonEncode({ 'data': { 'apikey': '12345678901234567890' } });

  print("Body: " + body);

  http.post(url,
      headers: {"Content-Type": "application/json"},
      body: body
  ).then((http.Response response) {
    print("Response status: ${response.statusCode}");
    print("Response body: ${response.contentLength}");
    print(response.headers);
    print(response.request);

  });
  }

I have a problem with the response from the request, where its suppose to have a body with json, but something went wrong and i think is with the json that i send on the body request, because it is a nested json object, and the value of the key is a json object. i would love to know how i can parse the json right and insert into body of the request.

this is the header response:

 {set-cookie: JSESSIONID=DA65FBCBA2796D173F8C8D78AD87F9AD;path=/testes2/;HttpOnly, last-modified: Thu, 10 May 2018 17:15:13 GMT, cache-control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0, date: Thu, 10 May 2018 17:15:13 GMT, content-length: 0, pragma: no-cache, content-type: text/html, server: Apache-Coyote/1.1, expires: Tue, 03 Jul 2001 06:00:00 GMT}

and this is how is suppose to be:

Server: Apache-Coyote/1.1
Expires: Tue, 03 Jul 2001 06:00:00 GMT
Last-Modified: Thu, 10 May 2018 17:17:07 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
Vary: Accept-Encoding
Set-Cookie: JSESSIONID=84813CC68E0E8EA6021CB0B4C2F245BC;path=/testes2/;HttpOnly
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked

the body response came empty and i think its because the body i sent on the request, can anyone help me with the nested json object in value??

SCREENSHOT OF POSTMAN:

TheMisir
  • 4,083
  • 1
  • 27
  • 37
n8bour
  • 1,109
  • 1
  • 9
  • 17
  • are your variable named intentionally `url` and `uri`? – David Fox May 10 '18 at 17:33
  • 2
    If you use `json.encode(...)` you send a string not JSON. Just send `{ 'data': { 'xpto': '12345678901234567890' } }` directly if you want it to be treated as JSON. – Günter Zöchbauer May 10 '18 at 17:33
  • @DavidFox yeah when i copy paste i forgot to change it, ty for notice. – n8bour May 10 '18 at 17:40
  • 1
    @GünterZöchbauer if i do that, this happens : E/flutter ( 1805): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception: E/flutter ( 1805): Bad state: Cannot set the body fields of a Request with content-type "application/json". E/flutter ( 1805): #0 Request.bodyFields= (package:http/src/request.dart:124:7) E/flutter ( 1805): #1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:165:17) E/flutter ( 1805): E/flutter ( 1805): #2 BaseClient.post (package:http/src/base_client.dart:56:5) ... – n8bour May 10 '18 at 17:43
  • Try to remove the line where you set the header to application/json. – Günter Zöchbauer May 10 '18 at 17:51
  • 1
    gave me this error : E/flutter ( 1805): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception: E/flutter ( 1805): type '_InternalLinkedHashMap' is not a subtype of type 'String' in type cast where E/flutter ( 1805): _InternalLinkedHashMap is from dart:collection E/flutter ( 1805): String is from dart:core E/flutter ( 1805): String is from dart:core E/flutter ( 1805): String is from dart:core – n8bour May 10 '18 at 18:03
  • Firstly, the header response is how you expect it to be. You are printing the `toString` of the Dart map. If you want to see each item, print them like this: `response.headers.forEach((a, b) => print('$a: $b'));`. If you pass the `http` package a `Map` it expects it to be a `Map` containing POST form entities, so you can't send json that way (unless you encode it yourself first). Can you update the question showing either some documentation of the web API or sample code in another language? – Richard Heap May 10 '18 at 18:29
  • @RichardHeap i dont have access to any kind of API documentation, this is my final degree project, im the first, using the API on a multi-platform app, and this is only the first phase of the request authentication process, i only know the results because in postman it works fine and i know what is suppose to come on the response and no i only try it to use in dart/flutter :/ I tried to use the package json_annotation and json_serializable but still or im doing the encoding wrong i think, the main problem is the vale of 'data' is another json object, i think the htt.post is not liking it. – n8bour May 11 '18 at 12:45
  • Adding the import for `http` would have made it more clear that your are using this package. – Günter Zöchbauer May 11 '18 at 13:03
  • Because you are talking about the response in your question, I guess what you actually want is adding the header `Accept: application/json` in addition to `'content-type'`. If this doesn't help it's unlikely to be related to Dart or the client code, but rather an issue with the server. – Günter Zöchbauer May 11 '18 at 13:05
  • @CláudioAlmeida Please update the question with a screenshot of the request as sent by Postman and/or your Postman workspace – Richard Heap May 11 '18 at 13:33
  • @GünterZöchbauer yeah i put it nothing happens, because like mentioned before i cannot send the body as json, so the response come wrong :S – n8bour May 11 '18 at 14:12
  • @RichardHeap Screenshots are there :D – n8bour May 11 '18 at 14:12
  • Why do you think the body is not sent as JSON? I think sending is working fine, just the server doesn't respond as you expect for some reason. I do the same here https://github.com/bwu-dart/bwu_docker/blob/0244aa90079e4e8515b5ca412e73b8f858cab809/lib/src/remote_api.dart#L91-L104 – Günter Zöchbauer May 11 '18 at 14:18
  • You've posted only the headers of the response. We need to see the headers and the body of the request. – Richard Heap May 11 '18 at 14:19
  • oh, I see it now – Richard Heap May 11 '18 at 14:20
  • @GünterZöchbauer because in postman or RESTclient of intelliJ i send the request and he responds fine, i put it some screenshots of postman with the correct response – n8bour May 11 '18 at 14:24
  • More helpful would be to see how the request from Dart differs from the RESTclient request. – Günter Zöchbauer May 11 '18 at 14:26
  • @GünterZöchbauer this is the link to the repo: https://github.com/avilio/ippdrive/blob/master/lib/RequestsAPI/requests.dart In RESTclient request, i just put content-type: application/json on header, and the json on body and the link on url and ofc put as a POST nothing special – n8bour May 11 '18 at 14:29
  • 1
    "If you use json.encode(...) you send a string not JSON" Thanks @GünterZöchbauer, I was using json.encode(...), removed, and now I'm getting the required response accordingly! – Abdisamad Khalif Sep 08 '18 at 15:55

13 Answers13

156

This works!

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;

Future<http.Response> postRequest () async {
  var url ='https://pae.ipportalegre.pt/testes2/wsjson/api/app/ws-authenticate';
  // body data
  Map data = {
    'apikey': '12345678901234567890'
  };
  // encode Map to JSON
  var body = json.encode(data);

  var response = await http.post(url,
      headers: {"Content-Type": "application/json"},
      body: body
  );
  print("${response.statusCode}");
  print("${response.body}");
  return response;
}
Mazen Embaby
  • 1,255
  • 11
  • 18
Raj Yadav
  • 9,677
  • 6
  • 35
  • 30
92

OK, finally we have an answer...

You are correctly specifying headers: {"Content-Type": "application/json"}, to set your content type. Under the hood either the package http or the lower level dart:io HttpClient is changing this to application/json; charset=utf-8. However, your server web application obviously isn't expecting the suffix.

To prove this I tried it in Java, with the two versions

conn.setRequestProperty("content-type", "application/json; charset=utf-8"); // fails
conn.setRequestProperty("content-type", "application/json"); // works

Are you able to contact the web application owner to explain their bug? I can't see where Dart is adding the suffix, but I'll look later.

EDIT Later investigation shows that it's the http package that, while doing a lot of the grunt work for you, is adding the suffix that your server dislikes. If you can't get them to fix the server then you can by-pass http and use the dart:io HttpClient directly. You end up with a bit of boilerplate which is normally handled for you by http.

Working example below:

import 'dart:convert';
import 'dart:io';
import 'dart:async';

main() async {
  String url =
      'https://pae.ipportalegre.pt/testes2/wsjson/api/app/ws-authenticate';
  Map map = {
    'data': {'apikey': '12345678901234567890'},
  };

  print(await apiRequest(url, map));
}

Future<String> apiRequest(String url, Map jsonMap) async {
  HttpClient httpClient = new HttpClient();
  HttpClientRequest request = await httpClient.postUrl(Uri.parse(url));
  request.headers.set('content-type', 'application/json');
  request.add(utf8.encode(json.encode(jsonMap)));
  HttpClientResponse response = await request.close();
  // todo - you should check the response.statusCode
  String reply = await response.transform(utf8.decoder).join();
  httpClient.close();
  return reply;
}

Depending on your use case, it may be more efficient to re-use the HttpClient, rather than keep creating a new one for each request. Todo - add some error handling ;-)

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • i will try to report to teacher and see if he is capable of find anyone to solve the problem... thank you very much for the answer i will try to find how to manage this aswel...thank you very much again for the time spent – n8bour May 11 '18 at 15:35
  • 3
    @CláudioAlmeida updated the answer with a working version, using the lower level `HttpClient` – Richard Heap May 11 '18 at 18:13
  • 2
    Richard Heap Thank you so much it works perfectly and ty again for the time spent!! it works wonderfully i will try to ask them to solve the charset request on sever!! You made my day, i was stuck here for 3 days and im getting demotivated !! tyvm – n8bour May 11 '18 at 18:39
  • 1
    @RichardHeap Thanks for the helpful answer. I had trouble sending a put request with nested json objects in the body with the regular http package, using the HttpClient class solved it. – Robert May 05 '19 at 05:36
  • I was having this issue, also mixed with allowing the CORS on the server. Causing an XMLHttpRequest error – david May 13 '20 at 16:45
  • `'content-type', 'application/json'` did it for me – Lexy Feito Aug 01 '20 at 15:58
  • this solution does not work for Flutter Web apps – Rushwin Jamdas Nov 30 '20 at 07:40
13

This would also work :

import 'package:http/http.dart' as http;

  sendRequest() async {

    Map data = {
       'apikey': '12345678901234567890'
    };

    var url = 'https://pae.ipportalegre.pt/testes2/wsjson/api/app/ws-authenticate';
    http.post(url, body: data)
        .then((response) {
      print("Response status: ${response.statusCode}");
      print("Response body: ${response.body}");
    });  
  }
Keshav Aditya R P
  • 3,001
  • 5
  • 21
  • 28
  • 2
    not only this works it also makes it clear that most of the times you don't need to json encode the data, thanks! – Edoardo Sep 11 '19 at 08:51
  • 4
    doesn't work for me: Bad state: Cannot set the body fields of a Request with content-type "application/json". – ejdrian313 Mar 01 '20 at 11:44
11

I think many people have problems with Post 'Content-type': 'application / json' The problem here is parse data Map <String, dynamic> to json:

Hope the code below can help someone

Model:

class ConversationReq {
  String name = '';
  String description = '';
  String privacy = '';
  String type = '';
  String status = '';

  String role;
  List<String> members;
  String conversationType = '';

  ConversationReq({this.type, this.name, this.status, this.description, this.privacy, this.conversationType, this.role, this.members});

  Map<String, dynamic> toJson() {

    final Map<String, dynamic> data = new Map<String, dynamic>();

    data['name'] = this.name;
    data['description'] = this.description;
    data['privacy'] = this.privacy;
    data['type'] = this.type;

    data['conversations'] = [
      {
        "members": members,
        "conversationType": conversationType,
      }
    ];

    return data;
  }
}

Request:

createNewConversation(ConversationReq param) async {
    HeaderRequestAuth headerAuth = await getAuthHeader();
    var headerRequest = headerAuth.toJson();
/*
{
            'Content-type': 'application/json',
            'x-credential-session-token': xSectionToken,
            'x-user-org-uuid': xOrg,
          }
*/

    var bodyValue = param.toJson();

    var bodydata = json.encode(bodyValue);// important
    print(bodydata);

    final response = await http.post(env.BASE_API_URL + "xxx", headers: headerRequest, body: bodydata);

    print(json.decode(response.body));
    if (response.statusCode == 200) {
      // TODO
    } else {
      // If that response was not OK, throw an error.
      throw Exception('Failed to load ConversationRepo');
    }
  }
Hưng Trịnh
  • 947
  • 1
  • 12
  • 23
10

this works for me

String body = json.encode(parameters);

http.Response response = await http.post(
  url: 'https://example.com',
  headers: {"Content-Type": "application/json"},
  body: body,
);
Dhanaji Yadav
  • 1,202
  • 1
  • 14
  • 22
9

I implement like this:

static createUserWithEmail(String username, String email, String password) async{
    var url = 'http://www.yourbackend.com/'+ "users";
    var body = {
        'user' : {
          'username': username,
          'address': email,
          'password': password
       }
    };

    return http.post(
      url, 
      body: json.encode(body),
      headers: {
        "Content-Type": "application/json"
      },
      encoding: Encoding.getByName("utf-8")
    );
  }
wilfredonoyola
  • 485
  • 5
  • 12
4

If your using freeze dont forget to encode it first. Which is probably easy to forget.

String body = json.encode(parameters.toJson()); //dont skip this one
http.Response response = await http.post(
  Uri.parse(apiURL),
  headers: {"Content-Type": "application/json"},
  body: body,
);
2

This code works for me:

static Future<LoginAPI> connectToAPI(String email, String password) async {
    String apiURL = "domain/login";

    Map<String, String> headers = {
      "Content-Type": "application/json",
      "Accept": "application/json",
    };

    var body = json.encode({"email": email, "password": password});

    var apiResult = await http.post(
      Uri.parse(apiURL),
      headers: headers,
      body: body,
    );

    var jsonObject = json.decode(apiResult.body);
    if (apiResult.statusCode >= 300) {
      return jsonObject // you can mapping json object also here
    } else {
      return jsonObject // you can mapping json object also here
    }
  }
prawito hudoro
  • 533
  • 1
  • 5
  • 10
1

This one is for using HTTPClient class

 request.headers.add("body", json.encode(map));

I attached the encoded json body data to the header and added to it. It works for me.

Hari Prasath
  • 246
  • 4
  • 6
1

In my case POST in the Flutter App is working on the Web, but not on Android devices or the emulator.

Flutter->Server->API

To fix it, I:

  1. changed HTTP HEADER on server with:

    $curlArr[CURLOPT_HTTPHEADER] = str_replace("application/x-www-form-urlencoded; charset=utf-8",
      "application/x-www-form-urlencoded",$curlArr[CURLOPT_HTTPHEADER]);
    
  2. copied most of the Headers to my App from postman.

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Ilias
  • 11
  • 1
1
String body = json.encode(parameters);

http.Response response = await http.post(
  url: Uri.parse('https://example.com'),
  headers: {"Content-Type": "application/json"},
  body: body,
);

This worked for me!

tentamdin
  • 19
  • 2
0

Send header in your http request it will work

  1. add this line ==>> enter code hereheaders: {"Content-Type": "application/json"}

final response = await http.post( url, headers: {"Content-Type": "application/json"}, body: body);

-4

In my case I forgot to enable

app.use(express.json());

in my NodeJs server.