I have to send a multipart HTTP post request that contains a image, body and header, Please find the body format
body: {
"Id":Id,
"Details": {
"name": Name,
"centroid": centroid,
"attribute": {
"boundaryOpacity": boundaryOpacity,
"boundaryWeight": boundaryWeight,
"boundaryColor": boundaryColor,
"labelColor": labelColor,
},
},}
headers: {
'tenantId': tenantId,
'Content-Type': 'multipart/form-data',
'x-access-token': token
},
I have to send image along with this request .Please help me with this.
You can convert your map into multipartRequest and set your headers in multipartRequest.
Future<void> addProject(Project project, [File? file]) async {
final url = Uri.parse('$baseUrl/api/projects');
final format = DateFormat('yyyy-MM-dd');
final completionDate = format.format(project.completionDate);
final data = {
'id': project.id,
'title': project.title,
'description': project.description,
'image': project.image,
'completion_date': completionDate,
};
try {
var request = http.MultipartRequest('POST', url);
request = jsonToFormData(request, data);
request.headers['X-Requested-With'] = "XMLHttpRequest";
request.headers['Authorization'] = "Bearer $authToken";
if (file != null) {
request.files
.add(await http.MultipartFile.fromPath("image", file.path));
}
final response = await request.send();
final responseData = await response.stream.toBytes();
final responseString = String.fromCharCodes(responseData);
print(responseString)
notifyListeners();
} catch (error) {
print('Error add project $error');
throw (error);
}
}
jsonToFormData(http.MultipartRequest request, Map<String, dynamic> data) {
for (var key in data.keys) {
request.fields[key] = data[key].toString();
}
return request;
}
You can't send json encoded string with multipart, you have to do it formdata way, you may need to update your backend code
final req = http.MultipartRequest('POST', url);
// Write your add files statement here
req.fields['id'] = id; // This is your id field
req.fields['details[name]'] = Name; // This is name field in details object
req.fields['details[attribute][boundaryOpacity]'] = boundaryOpacity; // This is how you write nested fields
You can follow same pattern for other fields as well, you need to implment a check for null field, assuming id can be null, write it as follows
req.fields['id'] = id != null ? id : ''; // If it is null convert it to empty string or don't include it
Alternate solution
Add a data field and convert entire payload to json string and decode at backend.
Related
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,
))
The request body is a form-data. The request body has a key, image that expects a file (image file here). This is what I've done so far:
final response = await http.MultipartRequest(
'POST', Uri.parse(Constant.baseUrl + 'api/patients'))
..headers.addAll({"Authorization": "Bearer $token"})
..fields.addAll({
"first_name": firstName,
"last_name": lastName,
"email": email,
"date_of_birth": dob,
"address": address,
"gender": gender,
"blood_group": bg,
"primary_phone": primaryPhone,
"alt_phone": altPhone
})
..files.add(await http.MultipartFile.fromPath(
'image',
image.path,
));
I'm attaching the postman request body here for better understanding of my question. The last key is the one I'm facing trouble with.
I think I'm just uploading the image file without the key. That is why, my request works but the image still doesn't get posted. The image file here is a XFile (using the image_picker library in FLutter).
Also I want to achieve this using the http library. I know it is possible through dio. But just for this request, I don't want to import another library.
uploadTest(FilePickerResult result, String url, docname, formId,
{lastService: false}) async {
print(result);
if (result != null) {
String filename = docname;
final file = result.files.first;
// if (lastService == true) {
filename = docname + '.' + file.name.split(".")[1];
print("filename is $filename");
// }
var uri = Uri.parse(url);
var stream = http.ByteStream(file.readStream);
var request = http.MultipartRequest('POST', uri);
final mimeType = lookupMimeType(file.name ?? '');
var val1 = {
"customerId": variableMap['customerId'],
"FormId": formId,
"docName": filename
};
final val2 = json.encode(val1);
request.fields['val'] = val2;
var multipartFile = http.MultipartFile(
'myFile',
stream,
file.size,
filename: filename,
contentType: mimeType == null ? null : MediaType.parse(mimeType),
);
request.files.add(multipartFile);
final httpClient = http.Client();
final response = await httpClient.send(request);
if (response.statusCode != 200) {
throw Exception('HTTP ${response.statusCode}');
}
final body = await response.stream.transform(utf8.decoder).join();
print(body);
}
}
// I am posting the method that i used ..havenot customized as your usecase. It performs the imageupload from the filePicker
http.MultipartRequest request = http.MultipartRequest(
"POST",
Uri.parse(baseUrl + "api/profiles/v1/update/"),
)
..fields['first_name'] = firstName
..fields['last_name'] = lastName
..fields['phone'] = phone
..fields['full_name'] = fullName
..fields['country'] = 'Uzbekistan'
..fields['city'] = 'Tashkent'
..fields['date_birth'] = dateBirth!;
request.files.add(await http.MultipartFile.fromPath(
'image',
image.path,
filename: image.path.split('/').last,
)
);
var response = await request.send();
var responsed = await http.Response.fromStream(response);
print(responsed.body);
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();
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());
});
}
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));