27

I'm trying to do a http post request and I need to specify the body as form-data, because the server don't take the request as raw.

This is what I'm doing:

import 'dart:convert';

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

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  postTest() async {
    final uri = 'https://na57.salesforce.com/services/oauth2/token';
    var requestBody = {
      'grant_type':'password',
      'client_id':'3MVG9dZJodJWITSviqdj3EnW.LrZ81MbuGBqgIxxxdD6u7Mru2NOEs8bHFoFyNw_nVKPhlF2EzDbNYI0rphQL',
      'client_secret':'42E131F37E4E05313646E1ED1D3788D76192EBECA7486D15BDDB8408B9726B42',
      'username':'example@mail.com.us',
      'password':'ABC1234563Af88jesKxPLVirJRW8wXvj3D'
    };

    http.Response response = await http.post(
        uri,
        body: json.encode(requestBody),
    );

    print(response.body);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Container(
        child: Center(
          child: RaisedButton(
            child: Text('Press Here'),
            onPressed: (){
              postTest();
            },
          ),
        ),
      ),
    );
  }
}

This is the actual response:

{
    "error": "unsupported_grant_type",
    "error_description": "grant type not supported"
}

Actual response

This is the expected response:

{
    "access_token": "00D0b000000Bb08!AR8AQO.s8mAGXCbwV77FXNLQqc2vtl8g6_16miVbgWlQMsuNHsaf2IGLUwnMVXBOfAj19iznhqhwlPOi4tagvf7FFgiJJgoi",
    "instance_url": "https://na57.salesforce.com",
    "id": "https://login.salesforce.com/id/00D0b000000Bb08EAC/0050b000005nstiAAA",
    "token_type": "Bearer",
    "issued_at": "1567993324968",
    "signature": "1+Zd/dSh9i7Moh2U0nFJLdXkVHqPlPVU6emwdYzXDPk="
}

Expected response

You can test this on postman switching the body between raw (you get the actual response) and form-data (you get the expected response)

PS: The headers are temporary headers created by the client tool.

M. Massula
  • 3,730
  • 2
  • 11
  • 14
  • Did you find the solution for the same?? Plz share – Araju Feb 12 '20 at 08:04
  • check this https://stackoverflow.com/a/67559979/6314955 – Malith Kuruwita May 16 '21 at 18:24
  • any solution for this? The above link from @MalithKuruwita doesn't have an accepted answer either – Eight Rice May 21 '21 at 16:29
  • @EightRice if you want to send data to the server which accepts content-type: x-www-form-urlencoded, the above URL that I gave works. And it is for text data. But if you want to send multipart/form-data. like file or images to the server (binary data), use this ( https://www.developerlibs.com/2020/07/flutter-upload-multipart-images-server.html ). That blog shows how to send text data as well. use the relevant one based on the server accepted content-type – Malith Kuruwita May 22 '21 at 00:43

12 Answers12

33

Use Map instead, because body in http package only has 3 types: String, List or Map. Try this:

final uri = 'https://na57.salesforce.com/services/oauth2/token';
var map = new Map<String, dynamic>();
map['grant_type'] = 'password';
map['client_id'] = '3MVG9dZJodJWITSviqdj3EnW.LrZ81MbuGBqgIxxxdD6u7Mru2NOEs8bHFoFyNw_nVKPhlF2EzDbNYI0rphQL';
map['client_secret'] = '42E131F37E4E05313646E1ED1D3788D76192EBECA7486D15BDDB8408B9726B42';
map['username'] = 'example@mail.com.us';
map['password'] = 'ABC1234563Af88jesKxPLVirJRW8wXvj3D';

http.Response response = await http.post(
    uri,
    body: map,
);
hoangquyy
  • 1,893
  • 2
  • 14
  • 29
  • 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". https://pub.dev/documentation/http/latest/http/Client/post.html – kaya Feb 10 '20 at 15:00
  • Seems using Map behaves more naturally like a post from an HTML form than the jsonEncode method used on the official flutter docs. Thanks. Fixed my issue. – Eaweb Oct 01 '20 at 13:04
  • 1
    map doesn't worked for me. I have to use Dio FormData, but I am wonder why I will get 400 error by sending a map ? – mohammad Nov 28 '20 at 13:31
  • 1
    @mohammad what did you sending? – hoangquyy Nov 30 '20 at 02:36
  • Bad state: Cannot set the body fields of a Request with content-type "application/json" – Akbar Pulatov Feb 18 '21 at 11:06
13

Use MultipartRequest class

A multipart/form-data request automatically sets the Content-Type header to multipart/form-data.

This value will override any value set by the user.

refer pub.dev doc here

For example:

 Map<String, String> requestBody = <String,String>{
     'field1':value1
  };
 Map<String, String> headers= <String,String>{
     'Authorization':'Basic ${base64Encode(utf8.encode('user:password'))}'
  };

  var uri = Uri.parse('http://localhost.com');
  var request = http.MultipartRequest('POST', uri)
    ..headers.addAll(headers) //if u have headers, basic auth, token bearer... Else remove line
    ..fields.addAll(requestBody);
  var response = await request.send();
  final respStr = await response.stream.bytesToString();
  return jsonDecode(respStr);

Hope this helps

Mr Special
  • 1,576
  • 1
  • 20
  • 33
  • 1
    If someone's wondering like I was, you can use `await http.Response.fromStream(streamedResponse)` to get the usual response object from the streamedresponse – lordvcs Nov 05 '20 at 12:24
12

There is a dart package dio
it works like a charm, am using it as a standard to do http requests.
Please read the docs too on sending form data with dio package

import 'package:dio/dio.dart';    

postData(Map<String, dynamic> body)async{    
var dio = Dio();
try {
      FormData formData = new FormData.fromMap(body);
      var response = await dio.post(url, data: formData);
      return response.data;
    } catch (e) {
      print(e);
    }
}
kishea
  • 637
  • 1
  • 6
  • 17
4

So, you wanna send the body as form-data right? maybe you can try this? for me it's work

postTest() async {
    final uri = 'https://na57.salesforce.com/services/oauth2/token';
    var requestBody = {
      'grant_type':'password',
      'client_id':'3MVG9dZJodJWITSviqdj3EnW.LrZ81MbuGBqgIxxxdD6u7Mru2NOEs8bHFoFyNw_nVKPhlF2EzDbNYI0rphQL',
      'client_secret':'42E131F37E4E05313646E1ED1D3788D76192EBECA7486D15BDDB8408B9726B42',
      'username':'example@mail.com.us',
      'password':'ABC1234563Af88jesKxPLVirJRW8wXvj3D'
    };

    http.Response response = await http.post(
        uri,
        body: requestBody,
    );

    print(response.body);
  }

Or

postTest() async {
    final uri = 'https://na57.salesforce.com/services/oauth2/token';

    http.Response response = await http.post(
        uri, body: {
      'grant_type':'password',
      'client_id':'3MVG9dZJodJWITSviqdj3EnW.LrZ81MbuGBqgIxxxdD6u7Mru2NOEs8bHFoFyNw_nVKPhlF2EzDbNYI0rphQL',
      'client_secret':'42E131F37E4E05313646E1ED1D3788D76192EBECA7486D15BDDB8408B9726B42',
      'username':'example@mail.com.us',
      'password':'ABC1234563Af88jesKxPLVirJRW8wXvj3D'
    });

    print(response.body);
  }
Maruzi Muslih
  • 53
  • 1
  • 6
3

Edit 1 (this worked for code login flow):

String url = "https://login.salesforce.com/services/oauth2/token";
http.post(url, body: {
  "grant_type": "authorization_code",
  "client_id": "some_client_id",
  "redirect_uri": "some_redirect_uri",
  "code": "some_code_generated_by_salesforce_login",
  "client_secret": "some_client_secret",
}).then((response) {
  //--handle response
});

give 'FormData' a try from:

import 'package:dio/dio.dart';

FormData formData = new FormData.fromMap(dataMap);

retrofitClient.getToken(formData).then((response){//--handle respnse--});

'retrofitClient' is from package retrofit: ^1.0.1+1

AngryBug
  • 86
  • 7
2

Can you try this;

    String url = 'https://myendpoint.com';
      Map<String, String> headers = {
"Content-Type": "application/x-www-form-urlencoded"    
"Content-type": "application/json"};
      String json = '{"grant_type":"password",
        "username":"myuser@mail.com",
        "password":"123456"}';
      // make POST request
      Response response = await post(url, headers: headers, body: json);
      // check the status code for the result
      int statusCode = response.statusCode;
      // this API passes back the id of the new item added to the body
      String body = response.body;
secret
  • 742
  • 1
  • 7
  • 24
2

This is my example with form data function

 Future<ResponseModel> postWithFormData(String url, List<File> files,
      {Map<String, String> body = const {}, bool throwAlert = false}) async {
    var request = http.MultipartRequest("POST", Uri.parse(localApiHost + url));

    request.headers
        .addAll({"Authorization": "Bearer ${Storage.getString(token)}"});
    request.fields.addAll(body);
    for (var file in files) {
      request.files.add(await http.MultipartFile.fromPath("files", file.path));
    }
    var sendRequest = await request.send();
    var response = await http.Response.fromStream(sendRequest);
    final responseData = json.decode(response.body);
    if (response.statusCode >= 400 && throwAlert) {
      showErrorDialog(responseData["message"]);
    }
    return ResponseModel(body: responseData, statusCode: response.statusCode);
  }
Akif Kara
  • 442
  • 6
  • 14
1

HTTP does not support form-data yet!

Use DIO instead. It will handle everything on its own!

M.AQIB
  • 359
  • 5
  • 11
1

This code snippets successfully executes a POST api call which expect an authorization token and form-data.

final headers = {'Authorization': 'Bearer $authToken'};
  var requestBody = {
    'shopId': '5',
    'fromDate': '01/01/2021',
    'toDate': '01/10/2022',
  };

  final response = await http.post(
    Uri.parse(
        'https://api.sample.com/mobile/dashboard/getdetails'),
    headers: headers,
    body: requestBody,
  );

  print("RESPONSE ${response.body}");
Jashan PJ
  • 4,177
  • 4
  • 27
  • 41
0

Using POSTMAN to test the query and get the format is quite useful. This is allow you to see if you really need to set Headers. See my example below. I hope it helps and it is not too much

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

class RegisterUser{    
  String fullname;
  String phonenumber;
  String emailaddress;
  String password;
  Map data;    
  RegisterUser({this.fullname, this.phonenumber, this.emailaddress, this.password});    
  Future<void> registeruseraction() async {    
    String url = 'https://api.url.com/';
    Response response = await post(url, body: {
      'fullname' : fullname,
      'phonenumber' : phonenumber,
      'emailaddress' : emailaddress,
      'password' : password
    });    
    print(response.body);
    data = jsonDecode(response.body);    
  }    
}
Lightwaxx
  • 625
  • 1
  • 8
  • 18
0

You can also use MultiPartRequest, it will work for sure

  var request = new 
  http.MultipartRequest("POST",Uri.parse("$baseUrl/example/"));

  request.headers.addAll(baseHeader);
  request.fields['id'] = params.id.toString();
  request.fields['regionId'] = params.regionId.toString();
  request.fields['districtId'] = params.districtId.toString(); 
  http.Response response = await http.Response.fromStream(await 
  request.send());

  print('Uploaded! ${response.body} ++ ${response.statusCode}');
 
0

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

// Function to make the POST request
Future<http.Response> post(String url, Map<String, String> body) async {
  // Encode the body of the request as JSON
  var encodedBody = json.encode(body);

  // Make the POST request
  var response = await http.post(url,
      headers: {"Content-Type": "application/json"}, body: encodedBody);

  // Return the response
  return response;
}