I have a button which when clicked, prints out response.
This is how the response is
{
"status": "success",
"user": "Worked well"
}
when I test it with postman it works fine, but when I try it from my flutter project, I get this error
I/flutter ( 5147): Response: - Instance of 'Response'
I/flutter ( 5147): FormatException: Unexpected character (at character 1)
I/flutter ( 5147): <!DOCTYPE html>
I/flutter ( 5147): ^
This is my flutter code:
http.Response response = await http.post(
Uri.parse(url + 'testMe.php'),
headers: headers,
body: body,
);
print('response ${response}');
if (response.body.isNotEmpty) {
json.decode(json.encode(response.body));
} else {
print('Response is empty...');
}
One thing I noticed is that, sometimes it does not throw the error above in flutter, it works fine and sometimes it throws the error, so I don't why it happen that way.
Flu
Postman Header
This worked for me:
Map<String, String> headers = {
'Content-Type': 'application/json',
'Charset': 'utf-8',
};
Your api return you a html instead of json, you can do this to avoid getting FormatException:
http.Response response = await http.post(
Uri.parse(url + 'testMe.php'),
headers: headers,
body: body,
);
print('response ${response}');
if (response.statusCode == 200) {
json.decode(response.body);
} else {
print('Response is empty...');
}
usually when statuscode is 500 or 404 this happened, when you check for status code 200, you can avoid getting this FormatException.
Also you don't need to encode the response and decode it again, your response is already encoded in server side, just decode it.
Related
I want to connect to a web service. When I use postman, request send and response receive successfully. But in the flutter app, I get error 422 in the android emulator. And with the same code in flutter web, I get XMLHttpRequest error.
My postman:
This is my data that send to the server:
var data = {
"username": usernameController.text,
"password": passwordController.text,
"email": emailController.text
};
And send a request with dio:
Response response = await client
.post(theUrl,
options: Options(headers: {
HttpHeaders.contentTypeHeader: "application/json",
HttpHeaders.acceptHeader:"*/*"
}),
data: jsonEncode(data))
.timeout(const Duration(seconds: 10));
I get errors on this method:
on DioError catch (error) {
var statusCode = error.response?.statusCode;
print("+++++++++++++++" + statusCode.toString());
print("+++++++++++++++" + error.message);
}
How can I fix these errors?
I Added this code to options in dio and Error 422 in mobile is fixed:
Options(
validateStatus: (status) {
return status! < 500;
},
followRedirects: false,
...)
But I still get error XMLHttpRequest in flutter web.
I am new to Flutter development and I am trying to decode or translate special characters.
The example I am working with looks like this presented as normal text in flutter:
Example: "Déjà Vu" to "Déjà Vu"
The left is how it appears on the UI and the result I would like to see is on the right.
I have tried using Runes class via docs --> https://api.dart.dev/stable/1.24.3/dart-core/Runes-class.html but no luck.
This is the non-working code:
child: Text(new Runes("Déjà Vu").string)
Update:I tried to pass 'Content-type': 'application/json; charset=utf-8', in the API call, however it didn't seem to correct this particular issue. I will attach a snapshot of the response (I ran it with the new headers and also without)
Here is the code:
Future<http.Response> _attemptCall(String suffix) => http.get(
'$kBaseURL$suffix',
headers: {
'Authorization': 'Bearer $_accessToken',
'Content-type': 'application/json; charset=utf-8',
},
);
Future<T> _authorizedCall<T>(
String suffix,
T Function(String) decode,
) async {
if (_accessToken == '') {
await refreshToken();
}
http.Response response = await _attemptCall(suffix);
var resBody = response.body;
print('This is the response --> $resBody');
if (response.statusCode == 401) {
await refreshToken();
response = await _attemptCall(suffix);
}
if (response.statusCode == 200) {
return decode(response.body);
}
return null;
}
#override
Future<Episode> getEpisodeDetails(String id) => _authorizedCall(
_episodeDetailUrl(id),
(s) => Episode.fromJson(jsonDecode(s)),
);
This charset mangling is called Mojibake (Thanks Randal Schwartz for pointing out!)
You cannot change "Déjà Vu" back to "Déjà Vu", you have to take action on either the way data is encoded and sent or the way you decode the response.
See the binary representation of these utf-8 characters:
11000011 10000011 Ã --> there is no way to tell `Ã` that it should be `à`
11000010 10101001 ©
11000011 10100000 à
11000011 10101001 é
You need to fix the problem upstream with the either the API response:
Content-type: application/json; charset=utf-8
The API is sending you back a stream of bytes, and should be utf8 in order to avoid this kind of output.
Or the way you decode the stream of bytes, you may also change:
return decode(response.body)
To
return decode(utf8.decode(response.bodyBytes));
When I give header and body information inside my http request, it redirects automatically to next page even if i dont give any login credential in my Mobile App.
Future<Login> fetchLoginData() async {
final http.Response response = await http.post(
'http://lmsapi.design.net:88//api/login/login',
headers: <String, String>{
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic XfnatLYBaDO2AKP6KfcIJg=='
},
/* body: {
'companycode': 'ABC1001',
'deploymentcode': 'ui11'
}*/
);
if (response.statusCode == 200) {
// then parse the JSON.
return Login.fromJson(jsonDecode(response.body));
} else {
// then throw an exception.
throw Exception('Failed to load Data');
}
}
Can someone help me how to pass header and body inside my http request?
The issue doesn't seem to be about handling the http response and leans more toward on how the navigation to the next page is handled. You'd need to check if fetchLoginData() returns the response. Future callback can be handled by using fetchLoginData().then() for example.
fetchLoginData().then((Login? loginData){
if(loginData!=null){
// Handle Navigation
}
});
Then you may consider implementing the navigation once you got a successful response from fetchLoginData()
I'm currently working on a project which like a lot of other projects works with s3 storage. In this case the storage is linked via the back-end.
The situation is like this, I can get the 'attachment' via an URL, lets say example.com/api/attachments/{uuid}. If the user is authorized (via the header Authorization) it should return a 302 statuscode and redirect to the s3 url. The problem is that after the redirect the Authorization header persists and the http client return a 400 response and it's because of the persisting Authorization header. Is there any way I can remove the Authorization header after redirect without catching the first request and firing a new one?
My http client code currently looks like this:
#override
Future get({
String url,
Map<String, dynamic> data,
Map<String, String> parameters,
}) async {
await _refreshClient();
try {
final response = await dio.get(
url,
data: json.encode(data),
queryParameters: parameters,
);
return response.data;
} on DioError catch (e) {
throw ServerException(
statusCode: e.response.statusCode,
message: e.response.statusMessage,
);
}
}
Future<void> _refreshClient() async {
final token = await auth.token;
dio.options.baseUrl = config.baseUrl;
dio.options.headers.addAll({
'Authorization': 'Bearer $token',
'Accept': 'application/json',
});
dio.options.contentType = 'application/json';
}
Good news! This has been fixed recently with Dart 2.16 / Flutter v2.10!
Related bugs in dart issue tracker:
https://github.com/dart-lang/sdk/issues/47246
https://github.com/dart-lang/sdk/issues/45410
Official announcement:
https://medium.com/dartlang/dart-2-16-improved-tooling-and-platform-handling-dd87abd6bad1
TLDR: upgrade to Flutter v2.10!
Looking at the Dio docs, it seems like this is intentional behaviour.
All headers added to the request will be added to the redirection request(s). However, any body send with the request will not be part of the redirection request(s).
https://api.flutter.dev/flutter/dart-io/HttpClientRequest/followRedirects.html
However, I understand (and agree!) that this is generally undesirable behaviour. My solution is to manually follow the redirects myself, which is not very nice but works in a pinch.
Response<String> response;
try {
response = await dio.get(
url,
options: Options(
// Your headers here, which might be your auth headers
headers: ...,
// This is the key - avoid following redirects automatically and handle it ourselves
followRedirects: false,
),
);
} on DioError catch (e) {
final initialResponse = e.response;
// You can modify this to understand other kinds of redirects like 301 or 307
if (initialResponse != null && initialResponse.statusCode == 302) {
response = await dio.get(
initialResponse.headers.value("location")!, // We must get a location header if we got a redirect
),
);
} else {
// Rethrow here in all other cases
throw e;
}
}
I am using http client for flutter network call.
My request working on postman getting response properly,
But while trying with http.post it returns error code 307-Temporary Redirect,
Method Body:
static Future<http.Response> httpPost(
Map postParam, String serviceURL) async {
Map tempParam = {"id": "username", "pwd": "password"};
var param = json.encode(tempParam);
serviceURL = "http:xxxx/Login/Login";
// temp check
Map<String, String> headers = {
'Content-Type': 'application/json',
'cache-control': 'no-cache',
};
await http.post(serviceURL, headers: headers, body: param).then((response) {
return response;
});
}
Also, the same code returns a proper response to other requests and URLs.
First I trying with chopper client but had same issue.
I am unable to detect that issue from my end of from server-side.
Any help/hint will be helpful
Try to put a slash / at the end of the serviceUrl. So, serviceUrl is serviceURL = "http:xxxx/Login/Login/" instead of serviceURL = "http:xxxx/Login/Login".
This works for me.
You need to find a way to follow redirect.
Maybe postman is doing that.
Read this >>
https://api.flutter.dev/flutter/dart-io/HttpClientRequest/followRedirects.html
Can you try with using get instead of post? At least to try and see what happend
In the documentation said:
Automatic redirect will only happen for "GET" and "HEAD" requests
only for the status codes
HttpStatus.movedPermanently (301),
HttpStatus.found (302),
HttpStatus.movedTemporarily (302, alias for HttpStatus.found),
HttpStatus.seeOther (303),
HttpStatus.temporaryRedirect (307)
keeping https instead of http in the URL it is helping me.
#Abel's answer above is correct but I had to switch from using:
Uri url = Uri.https(defaultUri, path);
to
Uri url = Uri.parse('https://todo-fastapi-flutter.herokuapp.com/plan/');
to get that last / after plan.
The first way kept dropping it so I was getting 307 errors.
flutter.dev shows a full example:
Future<http.Response> createAlbum(String title) {
return http.post(
Uri.parse('https://jsonplaceholder.typicode.com/albums'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': title,
}),
);
}
here: https://flutter.dev/docs/cookbook/networking/send-data#2-sending-data-to-server
For Dio Http Client, use Dio Option follow redirect as True
getDioOption(){
return BaseOptions(connectTimeout: 30, receiveTimeout: 30,
followRedirects: true);
}