M. Massula
M. Massula

Reputation: 4830

How make a http post using form data in flutter?

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':'[email protected]',
      '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.

Upvotes: 34

Views: 134403

Answers (12)

The same error message shown to me. I just add Authorization into the header. Example:


Before

Options header = Options(
        headers: {
          'Accept': '*/*',
         'Access-Control-Allow-Origin': '*',
          'Content-Type': 'multipart/form-data'
        },
        sendTimeout: const Duration(seconds: 120),
        receiveTimeout: const Duration(seconds: 30),
      );

After

Options header = Options(
        headers: {
          'Accept': '*/*',
         'Access-Control-Allow-Origin': '*',
    enter code here
          'Content-Type': 'multipart/form-data',
          ***'Authorization' : 'Bearer ${GetIt.I.get<LocalSharedPreferences>().getCurrentUserToken()}'***
        },
        sendTimeout: const Duration(seconds: 120),
        receiveTimeout: const Duration(seconds: 30),
      );

Then only the error 403 is gone. Then, I research a bit on it. The real problem is some API's not working without Authorization. In my case, I used laravel as my backend, for that I have used Laravel api. So, for laravel the authorization needed.

Upvotes: 0

hoangquyy
hoangquyy

Reputation: 2073

Use Map instead, because body in http package only has 3 types: String, List<int> or Map<String, String>.

Try this:

final Uri uri = Uri.parse('https://na57.salesforce.com/services/oauth2/token');
    final map = <String, dynamic>{};
    map['grant_type'] = 'password';
    map['client_id'] = '3MVG9dZJodJWITSviqdj3EnW.LrZ81MbuGBqgIxxxdD6u7Mru2NOEs8bHFoFyNw_nVKPhlF2EzDbNYI0rphQL';
    map['client_secret'] = '42E131F37E4E05313646E1ED1D3788D76192EBECA7486D15BDDB8408B9726B42';
    map['username'] = '[email protected]';
    map['password'] = 'ABC1234563Af88jesKxPLVirJRW8wXvj3D';

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

Upvotes: 38

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

Upvotes: 0

Akif Kara
Akif Kara

Reputation: 662

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

Upvotes: 2

Jafar Temirov
Jafar Temirov

Reputation: 59

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

Upvotes: 0

Jashan PJ
Jashan PJ

Reputation: 4408

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

Upvotes: 1

Maruzi Muslih
Maruzi Muslih

Reputation: 53

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':'[email protected]',
      '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':'[email protected]',
      'password':'ABC1234563Af88jesKxPLVirJRW8wXvj3D'
    });

    print(response.body);
  }

Upvotes: 4

Mr Special
Mr Special

Reputation: 1676

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

Upvotes: 14

Lightwaxx
Lightwaxx

Reputation: 765

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

Upvotes: 0

kishea
kishea

Reputation: 637

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

Upvotes: 12

AngryBug
AngryBug

Reputation: 116

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

Upvotes: 3

secret
secret

Reputation: 825

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":"[email protected]",
        "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;

Upvotes: 2

Related Questions