HTTP POST with Json on Body - Flutter/Dart
Asked Answered
T

13

94

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:

Talented answered 10/5, 2018 at 17:21 Comment(20)
are your variable named intentionally url and uri?Mckinney
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.Assisi
@DavidFox yeah when i copy paste i forgot to change it, ty for notice.Talented
@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): <asynchronous suspension> E/flutter ( 1805): #2 BaseClient.post (package:http/src/base_client.dart:56:5) ...Talented
Try to remove the line where you set the header to application/json.Assisi
gave me this error : E/flutter ( 1805): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception: E/flutter ( 1805): type '_InternalLinkedHashMap<String, String>' 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:coreTalented
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<String, String> 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?Bodywork
@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.Talented
Adding the import for http would have made it more clear that your are using this package.Assisi
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.Assisi
@CláudioAlmeida Please update the question with a screenshot of the request as sent by Postman and/or your Postman workspaceBodywork
@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 :STalented
@RichardHeap Screenshots are there :DTalented
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 github.com/bwu-dart/bwu_docker/blob/…Assisi
You've posted only the headers of the response. We need to see the headers and the body of the request.Bodywork
oh, I see it nowBodywork
@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 responseTalented
More helpful would be to see how the request from Dart differs from the RESTclient request.Assisi
@GünterZöchbauer this is the link to the repo: github.com/avilio/ippdrive/blob/master/lib/RequestsAPI/… 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 specialTalented
"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!Devotee
B
93

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

Bodywork answered 11/5, 2018 at 15:25 Comment(7)
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 spentTalented
@CláudioAlmeida updated the answer with a working version, using the lower level HttpClientBodywork
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 !! tyvmTalented
@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.Bailee
I was having this issue, also mixed with allowing the CORS on the server. Causing an XMLHttpRequest errorRhinoplasty
'content-type', 'application/json' did it for meKabuki
this solution does not work for Flutter Web appsSpatter
M
161

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';
  
  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;
}
Mesopause answered 5/3, 2019 at 10:5 Comment(8)
Thanks for the example! Is this documented anywhere? I wasted a lot of time until I found this example... :/Adriannaadrianne
It doesn't work. The content type header will end up as "text/plain; charset=utf-8"Garlicky
Had to encode my body to JSON to make it work. But great solution. Thanks.Pertussis
Now jsonEncode() function is supported by latest dart in flutter. Thanks.Perspicacity
I was having this issue, also mixed with allowing the CORS on the server. Causing an XMLHttpRequest errorRhinoplasty
Very useful Thank youSastruga
Works for me. I add more variable for headers just to make HTTP post more clear. Map<String, String> headers = { "Content-Type": "application/json", "Accept": "application/json", };Indication
Wasting 8 hours b/c of missing that dud header 😭Workmanlike
B
93

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

Bodywork answered 11/5, 2018 at 15:25 Comment(7)
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 spentTalented
@CláudioAlmeida updated the answer with a working version, using the lower level HttpClientBodywork
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 !! tyvmTalented
@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.Bailee
I was having this issue, also mixed with allowing the CORS on the server. Causing an XMLHttpRequest errorRhinoplasty
'content-type', 'application/json' did it for meKabuki
this solution does not work for Flutter Web appsSpatter
R
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}");
    });  
  }
Recti answered 11/9, 2018 at 9:33 Comment(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!Delphine
doesn't work for me: Bad state: Cannot set the body fields of a Request with content-type "application/json".Directions
E
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');
    }
  }
Eusebioeusebius answered 12/12, 2019 at 8:9 Comment(0)
S
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,
);
Sybilla answered 4/9, 2020 at 16:25 Comment(1)
not working . {"Message":"The request entity's media type 'text/plain' is not supported for this resource."}Isometrics
A
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")
    );
  }
Appendicitis answered 4/6, 2020 at 16:57 Comment(3)
Thanks man, I implement "encoding: Encoding.getByName("utf-8")" and resolve my issueReconnaissance
Encoding is utf8 by default, so specifying that would have no effect (over not specifying)Affectionate
I was missing "Content-Type": "application/json" as I have the json() middleware in express endpoint, thanksBalakirev
I
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,
);
Infatuate answered 15/9, 2022 at 3:28 Comment(0)
I
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
    }
  }
Indication answered 24/6, 2022 at 0:16 Comment(0)
S
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.

Smokechaser answered 20/6, 2019 at 9:49 Comment(0)
K
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.

Kean answered 23/4, 2021 at 14:45 Comment(0)
C
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!

Caresa answered 23/4, 2021 at 21:18 Comment(0)
G
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);

Greige answered 26/5, 2023 at 11:34 Comment(0)
P
-5

In my case I forgot to enable

app.use(express.json());

in my NodeJs server.

Pulse answered 14/7, 2020 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.