Flutter: HttpClient post contentLength -- exception - flutter

Very weird...
In order to post some JSON data to my server, I define the contentLength to the length of the JSON encoded data but I then receive an exception that says "Content size exceeds specified contentLength". Difference is 1 byte.
Here is the source code:
Future<Map> ajaxPost(String serviceName, Map data) async {
var responseBody = json.decode('{"data": "", "status": "NOK"}');
try {
var httpClient = new HttpClient();
var uri = mid.serverHttps ? new Uri.https(mid.serverUrl, _serverApi + serviceName)
: new Uri.http(mid.serverUrl, _serverApi + serviceName);
var request = await httpClient.postUrl(uri);
var body = json.encode(data);
request.headers
..add('X-mobile-uuid', await _getDeviceIdentity())
..add('X-mobile-token', await mid.getMobileToken());
request.headers.contentLength = body.length;
request.headers.set('Content-Type', 'application/json; charset=utf-8');
request.write(body);
var response = await request.close();
if (response.statusCode == 200){
responseBody = json.decode(await response.transform(utf8.decoder).join());
//
// If we receive a new token, let's save it
//
if (responseBody["status"] == "TOKEN"){
await mid.setMobileToken(responseBody["data"]);
// Let's change the status to "OK", to make it easier to handle
responseBody["status"] = "OK";
}
}
} catch(e){
// An error was received
throw new Exception("AJAX ERROR");
}
return responseBody;
}
Some other times, it works fine...
Am I doing anything wrong with this code?
Many thanks for your help.
EDITED WITH SOLUTION:
Many thanks for your help. The simply fact of using utf8.encode(json.encode(data)) did not fully work. So, I turned to the http library and it now works like a charm. The code is even lighter!
Here is the new version of the code:
Future<Map> ajaxPut(String serviceName, Map data) async {
var responseBody = json.decode('{"data": "", "status": "NOK"}');
try {
var response = await http.put(mid.urlBase + '/$_serverApi$serviceName',
body: json.encode(data),
headers: {
'X-mobile-uuid': await _getDeviceIdentity(),
'X-mobile-token': await mid.getMobileToken(),
'Content-Type': 'application/json; charset=utf-8'
});
if (response.statusCode == 200) {
responseBody = json.decode(response.body);
//
// If we receive a new token, let's save it
//
if (responseBody["status"] == "TOKEN") {
await mid.setMobileToken(responseBody["data"]);
// Let's change the status to "OK", to make it easier to handle
responseBody["status"] = "OK";
}
}
} catch (e) {
// An error was received
throw new Exception("AJAX ERROR");
}
return responseBody;
}

I got it working with
req.headers.contentLength = utf8.encode(body).length;
From an indirect tip of the Utf8Codec documentation which states
decode(List codeUnits, { bool allowMalformed }) → String
Decodes the UTF-8 codeUnits (a list of unsigned 8-bit integers) to the corresponding string.
That means thatutf8.encode() returns codeUnits which actually means List<uint8>.
Encoding a String payload would in theory return a list which length is the length of the payload in bytes.
So using httpClient means to always measure the length of the payload in bytes, not the length of a String which may differ.

Günter is right. Content-Length has to be the length of the byte array after encoding from a String to bytes in whatever encoding you server requires.
There's a package called http which provides a slightly higher level api (it uses dart.io httpClient under the hood) which takes care of encoding the post body and length for you. For example, when you need to send application/x-www-form-urlencoded form it will even take a Map and do all the encoding for you (you still need to encode to json yourself). It's equally happy to send just a String or List<int>. Here's an example:
Map<String, String> body = {
'name': 'doodle',
'color': 'blue',
'teamJson': json.encode({
'homeTeam': {'team': 'Team A'},
'awayTeam': {'team': 'Team B'},
}),
};
Response r = await post(
url,
body: body,
);

Seems your string contains multibyte characters.
UTF8-encode the string to get the correct length:
var body = utf8.encode(json.encode(data));

Related

Http Request Flutter didn't have body

I have a project with Flutter. And I want to get data from API. In my other project, I don't have any problem. But in this project, I have a problem. When I debug the process, in HTTP response didn't have body from API.
In class AuthService to get the API.
Future<ResponseCheckNIP> checkNIP({String? nip}) async {
var url = '$baseUrl/check-nip-new/$nip';
var header = {
'Content-Type': 'application/json',
};
// var body = jsonEncode({'nip': nip});
var response = await http.get(Uri.parse(url), headers: header);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
ResponseCheckNIP responseCheckNIP = ResponseCheckNIP.fromJson(data);
return responseCheckNIP;
} else {
throw Exception('Get NIP Failed');
}
}
And when I debug it, I get this
as we see, there is no body in there. Am I did something wrong?
If you look closely, the data is actually in the response.bodyBytes.
And Since you cannot directly convert bytes to json with dart, convert bytes to String first then decode the String using jsonDecode.
Below is the modified code.
Future<ResponseCheckNIP> checkNIP({String? nip}) async {
var url = '$baseUrl/check-nip-new/$nip';
var header = {
'Content-Type': 'application/json',
};
var response = await http.get(Uri.parse(url), headers: header);
if (response.statusCode == 200) {
// Get body bytes from response
final bytes = response.bodyBytes;
// Convert bytes to String then decode
final data = jsonDecode(utf8.decode(bytes));
ResponseCheckNIP responseCheckNIP = ResponseCheckNIP.fromJson(data);
return responseCheckNIP;
} else {
throw Exception('Get NIP Failed');
}
}
Hope this helps.
Thank you.

How to 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 or params.
here is the code I tried
** Future getApiResponse(url) async {
try {
// fetching data from the url
final response = await http.get(Uri.parse(url));
// checking status codes.
if (response.statusCode == 200 || response.statusCode == 201) {
responseJson = jsonDecode(response.body);
// log('$responseJson');
}
// debugPrint(response.body.toString());
} on SocketException {
throw FetchDataException(message: 'No internet connection');
}
return responseJson;
}
}
but its not working. here is the post man request
enter image description here
its not working on parms. only in body. its because this is in form data I guess.
how do I call form data in flutter using HTTP post?
First of all you can't send request body with GET request (you have to use POST/PUT etc.) and you can use Map for request body as form data because body in http package only has 3 types: String, List or Map. Try like this:
var formDataMap = Map<String, dynamic>();
formDataMap['username'] = 'username';
formDataMap['password'] = 'password';
final response = await http.post(
Uri.parse('http/url/of/your/api'),
body: formDataMap,
);
log(response.body);
For HTTP you can try this way
final uri = 'yourURL';
var map = new Map<String, dynamic>();
map['device-type'] = 'Android';
map['username'] = 'John';
map['password'] = '123456';
http.Response response = await http.post(
uri,
body: map,
);
I have use dio: ^4.0.6 to create FormData and API Calling.
//Create Formdata
formData = FormData.fromMap({
"username" : "John",
"password" : "123456",
"device-type" : "Android"
});
//API Call
final response = await (_dio.post(
yourURL,
data: formData,
cancelToken: cancelToken ?? _cancelToken,
options: options,
))

Incomplete response JSON body with flutter http.Request.send()

I am not sure what is going on with my function. I am trying to have a generic request function that I can use with any request method (GET, POST, etc...). Everything is working well, except for the POST response, it is missing data. I double checked what is expected to be returned and compared it with the response from Postman and my code. My code produces less data than expected and Postman.
Here is what I am doing
class HttpClientHandler {
String baseUrl = 'jsonplaceholder.typicode.com';
String path = '/posts';
static const Map<String, String> defaultHeaders = {'': ''};
static const Map<String, String> defaultBody = {'': ''};
Future<dynamic> request(HttpMethod method,
{Map<String, String> headers = defaultHeaders,
Map<String, dynamic> body}) async {
var uri = Uri.https(baseUrl, '$path');
var request = http.Request(method.type, uri);
request.headers.addAll(headers);
request.body = body.toString();
var response = await request.send();
if (response.statusCode >= 200 && response.statusCode <= 299) {
String rawData = await response.stream.transform(utf8.decoder).join();
return jsonDecode(rawData);
} else {
throw Exception();
}
}
}
The caller simply does the following:
var _client = HttpClientHandler();
var data = await _client.request(HttpMethod.POST, body: {'title': 'foo', 'body': 'bar', 'userId': 1});
print(data);
The response I get is:
{id: 101}
The expected response is:
{
"title": "foo",
"body": "bar",
"userId": "1",
"id": 101
}
I am using import 'package:http/http.dart' as http; package. Is this a stream, transform, or headers issue?
I found what was wrong in my implementation, I didn't jsonEncode my request body. The test API I am using here replies the same request body it receives. Since I wasn't encoding the body correctly, it only was recognizing the first data being sent.
So, the fix is to change this line:
request.body = body.toString();
to this line:
request.body = jsonEncode(body);
Also, I ended up parsing the stream byte to string differently, from:
String rawData = await response.stream.transform(utf8.decoder).join();
To:
String rawData = await response.stream.bytesToString();

How to send a png file by 'application/octet-stream' in Dart/Flutter to Microsoft Custom Vision?

I know this question could be redundant, but I am trying to send a png file through a POST request to Microsoft Custom Vision in Flutter.
This is my code:
void _makeRequest (File file) async {
String url = "<url>";
Map<String, String> headers = {
"Content-Type": "application/octet-stream",
"Prediction-Key": "<key>",
};
var bytes = file.readAsBytesSync();
var response = await http.post(
url,
headers: headers,
body: bytes,
);
print(response.body);
print(response.statusCode);
}
And when I run this code I get this response:
{"code":"ErrorUnknown","message":"The request entity's media type 'appliction/octet-stream' is not supported for this resource."}
Based on your comment , I think you used the wrong endpoint/URL. Since you're sending image, you have to use the other prediction endpoint that looks like this:
"https://southcentralus.api.cognitive.microsoft.com/customvision/v3.0/Prediction/<Project ID>/classify/iterations/<Iteration number>/image
^ Note ../image
If still can't, please try below code snipper(works for me):
final bytes = file.readAsBytesSync();
var uri = Uri.parse(
"<Prediction endpoint>");
var request = new http.Request("POST", uri)
..headers['Prediction-Key'] = "<Prediction Key>"
..headers['Content-Type'] = "application/octet-stream"
..bodyBytes = bytes;
http.Response response = await http.Response.fromStream(await request.send());
print(request);
print("Result: ${response.statusCode}");
print(response.statusCode);
print(response.body);

How To deal with Response after post request dart httpClient

So I was having issues with flutter http package when it came to making a post request so I used dart HttpClient. I made a post request according to what was described somewhere but I am having issues getting response. Here is my code
Future<HttpClientResponse> submit() async {
print('start');
Map<String, dynamic> data = { 'title' : 'My first post' };
String jsonString = json.encode(data); // encode map to json
String paramName = 'param'; // give the post param a name
String formBody = paramName + '=' + Uri.encodeQueryComponent(jsonString);
List<int> bodyBytes = utf8.encode(formBody); // utf8 encode
HttpClientRequest request =
await HttpClient().postUrl(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
// it's polite to send the body length to the server
request.headers.set('Content-Length', bodyBytes.length.toString());
request.headers.set('Content-Type', 'application/json');
request.add(bodyBytes);
print('done');
return await (request.close());
}
How do I get the response from this request?
HttpClientResponse response = await request.close();
response.transform(utf8.decoder).listen((contents) {
print(data); // <- response content is here
});
This will return HttpCLientResponse, more info https://api.dartlang.org/stable/2.6.1/dart-io/HttpClient-class.html
I have found this from the docs
new HttpClient().get('localhost', 80, '/file.txt')
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
response.transform(utf8.decoder).listen((contents) {
// handle data
});
});
Or Use http library
I have create a common method which can handle all get Request,
Future<String> getRequest([var endpoints, var queryParameters]) async {
var uri = Uri.https(NetworkUrl.BASE_URL_1, endpoints, queryParameters);
uri.replace(queryParameters: queryParameters);
var response =
await http.get(Uri.encodeFull(uri.toString()));
//Retrun reponse here
if (response.statusCode == 200) return response.body;
}
To get a response from the above method,
Future<String> deletePostApi() async {
await NetworkRepository()
.getRequest(NetworkUrl.deletePost + '${widget.mFeedData.post_id}')
.then((value) {// <=value is json respone
var dataConvertedToJSON = json.decode(value);
print("checkEmailResp" + dataConvertedToJSON.toString());
});
}