Invalid Header Name In Flutter HTTP Request - flutter

I have a login page where i am trying to send a login request to my backend. But I get an Unhandled Exception: Invalid header field name. Here is my submit function
submit() async {
var res = await LoginAPI().loginData(
{'email': _emailController.value, 'password': _passwordController.value});
var body = json.decode(res.body);
print(body);
}
Then in my LoginAPI class here is my loginData function that makes the call to the backend
import 'dart:convert';
import 'package:http/http.dart' as http;
class LoginAPI {
final String _url = "http://10.0.2.2:8000/api/";
Map<String, String> headers = {"Content-type": "application/json"};
loginData(data) async {
var fullUrl = _url + "v1/users/login";
return await http.post(
fullUrl,
body: jsonEncode(data),
headers: headers
);
}
}
Here is my request through postman
Here is my response through postman
When I make the same request with Postman i get the response I am supposed to get. What Am i doing wrong?

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

It looks from your postman request that you are just sending form data (not a json encoded body). package:http will form encode the body for you (and add the content type header) if you do the following:
return await http.post(
fullUrl,
body: data,
);

So i was able to solve the issue. The issue was with my CORS middleware on my server. I just made some changes and it worked fine. So if anyone has this issue just know it has nothing to do with flutter but most probably CORS

Related

how to send a GET request with Json body in Flutter?

I need to send a GET HTTP request with a JSON body. I know that this is not allowed by the RestFul specs. However, there is no chance to change the server. Is there a way to overcome this restriction in Flutter?
This is the code I am trying to use but I couldn't find a way to insert the body.
String urlGetkey = "https://pa.me/transactions/card_hash_key";
Map userHeader = {
"Content-type": "application/json",
"Accept": "application/json",
"User-Agent": "curl/7.64.",
};
var _body = jsonEncode({"api_key": zulu});
var request = http.Request('GET', Uri.parse(urlGetkey));
request.body = _body;
final streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
'''
Thanks in advance
You are close, but you need to use a subclass of BaseRequest so that you can add the body, which you can do by grabbing its sink and adding the body to that before then sending the request.
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() async {
final payload = '{"api_key":"ak_test_46po64VBDm3aqJmR8oXvFLqQi0VxtP"}';
final request = http.StreamedRequest(
'GET',
Uri.https(
'api.pagar.me',
'/1/transactions/card_hash_key',
),
);
request.headers['content-type'] = 'application/json';
request.sink
..add(utf8.encode(payload))
..close();
final streamedResponse = await request.send();
final response = await http.Response.fromStream(streamedResponse);
print(response.statusCode);
print(response.body);
}

Submit array in http flutter

This is how I post the two values to server using postman.
How should I write in http?
var url = "xxx";
var response = await http.post(url, headers: headers, body: {
....
'all_receivers': adminList.toString(),
'commence_time': endTime, // this no issue
...
});
I pass adminList which is [725,607], but get error:
Error
all_receivers id [725 is invalid. all_receivers id 607] is invalid.
You create a json and pass it
or
You can create and array of strings
You can simply resolve that by casting your List to String.
By casting the token list using toString() method you will get a String like this "['token1','token2']"
Here's the modified code:
List<String> adminList=["725","607"]; // how to pass 725,607
var response = await http.post(url, headers: headers, body: {
'all_receivers': adminList.toString(), // how to pass 725,607
}
try to pass with jsonEncode
var response = await http.post(url, headers: headers, body: JsonEncode({
'all_receivers': adminList,
});
print(response);
This is how I fixed
final receivers = adminList.join(",");
final body = {'all_receivers':receivers}
http.post(url', body: body);

Reusing http headers

I'm trying to find a best way to reuse http headers in my http responses. Instead of writing it in string literal
final http.Response response = await http.post(APIPath.somePath(),
headers:{"Content-Type": "application/json","Authorization": "Bearer $_token"},
body: json.encode(body));
I have made a custom class and get each header into a static function
class APIHeader {
static Map<String, String> json() => {"Content-Type": "application/json"};
static Map<String, String> form() => {"Content-Type": "multipart/form-data"};
static Map<String, String> authorization(String token) =>
{"Authorization": "Bearer $token"};
}
and call them wherever I need them which work great if there is only one header needed
final http.Response response = await http.put(APIPath.somePath(),
headers: APIHeader.json(), body: json.encode(body));
However I'm having a trouble if I need more then one header. I tried this..
final header = {}
..addAll(APIHeader.authorization(_token))
..addAll(APIHeader.json());
final http.Response response = await http.post(APIPath.somePath(),
headers: header, body: json.encode(body));
which gives me an error
Unhandled Exception: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'
Anyone have better idea how to reuse the headers?
Thanks to #pskink I found using mergeMaps from 'package:collection/collection.dart' the best way to reuse headers and merge them into one map
final http.Response response = await http.post(APIPath.somePath(),
headers: [APIHeader.authorization(_token), APIHeader.json()]
.reduce(mergeMaps),
body: json.encode(body));

How to post x-www-form-urlencoded in Flutter

I am trying to send a POST request to an API to create an account.
The request is working well, it should look like this :
Bulk Edit Mode :
Key-Value Edit mode :
There are also 9 headers that are auto-generated, so I did not show them, but I can take another screen if you need to.
My request looks like this :
import 'dart:convert' as convert ;
import 'package:my_project/requests/utils.dart';
import 'package:http/http.dart' as http;
Future<String> createUser(String firstName, String name, String mail,
String password, String confirmPassword, String birthDate,
String phone) async {
String url = BASE_URL + "createUser" ; // Don't worry about BASE_URL, the final url is correct
Map<String, dynamic> formMap = {
"name": name,
"surname": firstName,
"mail": mail,
"password": password,
"birth": birthDate,
"phone": phone,
"confirmPassword": confirmPassword
} ;
http.Response response = await http.post(
url,
body: convert.jsonEncode(formMap),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
encoding: convert.Encoding.getByName("utf-8"),
);
print("RESPONSE ${response.statusCode} ; BODY = ${response.body}");
return (response.body) ;
}
Here is my print result :
I/flutter ( 6942): RESPONSE 307 ; BODY =
As you can see, I am getting a 307 error, and the problem does not come from the server, as it worked with Postman.
Am I sending this form-urlencoded POST request correctly ?
I also tried :
http.Response response = await http.post(
url,
body: "name=$name&surname=$firstName&mail=$mail&password=$password&birth=$birthDate&phone=$phone&confirmPassword=$confirmPassword",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
encoding: convert.Encoding.getByName("utf-8"),
);
but with the same results. I tried too :
http.Response response = await http.post(
url,
body: formMap,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
encoding: convert.Encoding.getByName("utf-8"),
);
with same result again.
What am I doing wrong ?
EDIT :
I tried FoggyDay answer, here is my request now :
final client = HttpClient() ;
final request = await client.postUrl(Uri.parse(url));
request.headers.set(HttpHeaders.contentTypeHeader, "application/x-www_form-urlencoded");
request.followRedirects = true ;
request.write(formMap);
final response = await request.close();
print("STATUS CODE = ${response.statusCode}");
However I still have a 307 error. Did I create the right request ?
EDIT 2 :
As asked, I printed location as follow :
final client = HttpClient() ;
final request = await client.postUrl(Uri.parse(url));
request.headers.set(HttpHeaders.contentTypeHeader, "application/x-www_form-urlencoded");
request.followRedirects = true ;
request.write(formMap);
final response = await request.close();
print("STATUS CODE = ${response.statusCode}");
print("Response headers = ${response.headers}");
And I get :
I/flutter ( 7671): STATUS CODE = 307
I/flutter ( 7671): Response headers = location: /app/createUser/
I/flutter ( 7671): date: Tue, 26 May 2020 09:00:29 GMT
I/flutter ( 7671): content-length: 0
I/flutter ( 7671): server: Apache/2.4.41 (Amazon) OpenSSL/1.0.2k-fips
The thing is I am already making a call on /app/createUser... ('/app/' is in BASE_URL)
For x-www-form-urlencoded parameters, just use this:
Future<String> login(user, pass) async {
final response = await http.post(
Uri.parse('https:youraddress.com/api'),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
encoding: Encoding.getByName('utf-8'),
body: {"title": "title"},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
}
}
official http package from flutter is buggy with urlencoded type, you can use Dio package instead.
final dio = Dio();
final res = dio.post(
'/info',
data: {'id': 5},
options: Options(contentType: Headers.formUrlEncodedContentType),
);
As you can see, I am getting a 307 error, and the problem does not come from the server, as it worked with Postman.
No, that's NOT necessarily the case. Look here:
MDN: 307 Temporary Redirect
In other words, Postman is following the redirect ... and your Flutter app isn't.
SUGGESTION: Try setting followRedirects to true:
https://api.flutter.dev/flutter/dart-io/HttpClientRequest/followRedirects.html
ADDITIONAL INFO:
The default value for request.followRedirects happens to be "true" anyway. It doesn't hurt to explicitly set it ... but it explains why the behavior didn't change.
Per this post:
The Dart HTTP client won't follow
redirects
for POSTs unless the response code is 303. It follows 302 redirects
for GET or HEAD.
Per this post
The correct way to handle redirects on POST requests is to manually
implement an appropriate strategy for your use case:
var response = await client.post(...);
if (response.statusCode == 301 || response.statusCode == 302) {
// send post request again if appropriate
}
let try with this code, it works well for me.
var headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
var request = http.Request('POST', Uri.parse('https://oauth2.googleapis.com/token'));
request.bodyFields = {
'client_id': '',
'client_secret': '',
'code': '',
'grant_type': 'authorization_code',
'redirect_uri': ''
};
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
}
else {
print(response.reasonPhrase);
}
If you are using http, you should add the below lines
Android -
android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="first_app"
android:usesCleartextTraffic="true" //this line
android:icon="#mipmap/ic_launcher">
iOS -
ios/Runner/info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Be warned that you will need to have an explanation for Apple's review team when enabling this, otherwise, your app will get rejected on submission.
Uninstall the app and Reinstall again if you have the app already in the emulator when you add those lines to avoid confusions.
If you send HTTP GET request, you can use query parameters as follows:
QueryParameters
http://example.com/path/to/page?name=ferret&color=purple
The contents are encoded as query parameters in the URL
application/x-www-form- urlencoded
The contents are encoded as query parameters in the body of the
request instead of the URL.
The data is sent as a long query string. The query string contains
name-value pairs separated by & character
POST example
flutter http package version - http: ^0.13.1
import 'package:http/http.dart' as httpClient;
Future<dynamic> postData() async {
//Uri.https("192.168.1.30:5000", "/api/data")
//Uri.parse("your url");
final Uri uri = Uri.http("192.168.1.30:5000", "/api/data");
final response = await httpClient.post(
uri,
body: {
"name": "yourName",
"age": "yourAge"
},
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
encoding: Encoding.getByName('utf-8'),
);
return response.body;
}

How to make API call in flutter with header key?

Suppose the host site is: :
https://dev.xyz.com
API header key: "x-api-key: 7462-3172-8773-3312-5819"
To register a new user you have to call PUT method: {{host}}/api/customer/
And the body is like this:
{"email": "test#example.net",
"password": "aabbccdd",
"Name": "John",
}
Now how do I accomplish this in flutter? I have searched through several tutorials and still in confusion.
Import the http package from dart library and alias it as http, reason for this aliasing is that you dont want to have .get() method suggestion everywhere in the file. So when you use it with http as http.get() it will give you suggestion for get, In which you can pass the parameter called headers.
Code goes as follows:
import 'package:http/http.dart' as http;
url = 'YOUR_URL';
var response = await http.get(
url,
headers: {HttpHeaders.authorizationHeader: TOKEN}, //an example header
);
In your case,
import 'dart:convert';
import 'dart:io';
import 'dart:async';
main() async {
String url =
'https://dev.xyz.com';
Map map = {
'data': {'apikey': '7462-3172-8773-3312-5819'},
};
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;
}
You need put the header on the http request. For example:
await put(urlApi + 'update/'+ customer.id, headers: {'token': token, 'content-type': 'application/json'},body: body);