I'm trying to send a post request in Flutter with DIO package.
Here is the request:
getSessionId() async {
var csrf = await getCsrftoken();
var dio = new Dio(new Options(
baseUrl: "http://xxxxxxx/accounts/login/",
connectTimeout: 5000,
receiveTimeout: 100000,
// 5s
headers: {
'Cookie': "csrftoken=" + csrf
},
contentType: ContentType.JSON,
// Transform the response data to a String encoded with UTF8.
// The default value is [ResponseType.JSON].
responseType: ResponseType.PLAIN
));
var response;
response = await dio.post("http://xxxxxxx/accounts/login/",
data: {
"username": "xxxxx",
"password": "xxxxx",
"csrfmiddlewaretoken" : csrf
},
options: new Options(
contentType: ContentType.parse("application/x-www-form-urlencoded")),
);
print("StatusCode: ");
print(response.statusCode);
print("Response cookie: "); //THESE ARE NOT PRINTED
print(response.headers);
}
After the request i get:
E/flutter ( 4567): [ERROR:flutter/shell/common/shell.cc(181)] Dart Error: Unhandled exception:
E/flutter ( 4567): DioError [DioErrorType.RESPONSE]: Http status error [302]
E/flutter ( 4567): #0 getSessionId (file:///C:/get_order/lib/main.dart:36:14)
E/flutter ( 4567): <asynchronous suspension>
From this request i only need to get the sessionid cookie, but the function stop with unhandled exception.
I solved this way:
Add followRedirects: false and validateStatus: (status) { return status < 500;} to the request. Like this:
var response = await Dio().post("http://myurl",
data: requestBody,
options: Options(
followRedirects: false,
validateStatus: (status) { return status < 500; }
),
);
This way you can get from the 302 every headers and other.
The Dart HTTP client won't follow redirects for POSTs unless the response code is 303. It follows 302 redirects for GET or HEAD.
You could see if you can stop the server sending the redirect in response to a (presumably) valid login request, and send a 200 instead.
Or you could try sending the login request as a GET by encoding the form fields into the URL, for example:
http://xxxxxxx/accounts/login/?username=xxxx&password=yyyy&csrfmiddlewaretoken=zzzz
You would have to URL encode any special characters in the parameters. Presumably, you'll want to use HTTPS too.
Finally, is the URL meant to end with /? It might be worth trying /accounts/login.
i got a similar problem and i solved it with adding header with "Accept":"application/json" . henceforth it will only return json data otherwise it will prompt to redirect with html url.
Redirections for 302 are made in response to GET or HEAD requests, never for POST. Sometimes server sends 302 in response to POST (that was in my case). In this case Dio throws exception you can catch - remember to check if server status code is 302 or maybe it's another error.
try{
await dio.post( _urlLogin,
data:{...},
options: Options(
contentType: ContentType.parse("application/x-www-form-urlencoded"),
)
);
}on DioError catch(error){
if(error.response.statusCode == 302){
// do your stuff here
}
I'm trying to use this to a webscraping... don't ask me why lol. I came from python/golang and I've already tried the http package, however i recommend you to use the dio package.
With dio I'm doing the following:
Scrapers.client = Dio();
// create a local cookie to handle with sites that force you to use it
var cookieJar = CookieJar();
// assign middlewares to be "session like"
Scrapers.client?.interceptors.add(CookieManager(cookieJar));
// assign a logging middleware
Scrapers.client?.interceptors.add(LogInterceptor(responseBody: false));
// set the default content type to this client
Scrapers.client?.options.contentType = Headers.formUrlEncodedContentType;
...
static Future<Response> handleRedirects(Response r, int statusCode) async {
var redirects = 0;
while (r.statusCode == statusCode) {
print("redirect #${redirects++}");
final redirecturl = r.headers['location']![0];
r = await Scrapers.client!.post(redirecturl,
options: Options(
followRedirects: false,
validateStatus: (status) {
return status! < 500;
}));
}
return r;
}
...
Response r = await Scrapers.client!.post(url,
data: payload,
options: Options(
followRedirects: false,
validateStatus: (status) {
return status! < 500;
}));
r = await Scrapers.handleRedirects(r, 302);
Note that it's just a simple approach. You can change it according with you needs.
in my case, this problem was solved by send the cookie with the header in the post method
and the problem is the API was response to me with HTML login page rather than JSON data.
and you will find the cookie key in the response header when you perform a si/log - in
and the status error code was 302
Related
I'm using Dio to do requests to a REST-api. When the authorization token expires, I refresh the token and resend the request. The code to do that looks like this:
onError: (DioError error, ErrorInterceptorHandler handler) async {
logger.d(errorLogString(error));
if (error.response?.statusCode == 401 && error.requestOptions.path != 'login' && _retries-- > 0) {
await authService.refreshToken();
error.response = await dio.request(
error.requestOptions.path,
data: error.requestOptions.data,
queryParameters: error.requestOptions.queryParameters,
options: Options(
method: error.requestOptions.method,
headers: error.requestOptions.headers,
),
);
}
return handler.next(error);
},
This works okay, but Dio still throws an error even if the new request is successful, which means that I have to catch and check the status code for each request. For example like this:
try {
final response = await dio.post(
'someUrl',
);
return Object.fromJson(response.data);
} catch (error) {
if(error is DioError && (error.response?.statusCode ?? 300) < 300){
return Object.fromJson(error.response!.data);
}
throw parseError(error);
}
Is there any way that I can prevent Dio from trowing an error at all when the request is successfull, or do I have to catch the error each time like I do above. In that case, is there a way to simplify the process of catching the error and returning the successful response?
I know this is an old question but in case anyone else is looking for the answer to this like I was today, you can use handler.resolve(response)
Using the above example:
onError: (DioError error, ErrorInterceptorHandler handler) async {
logger.d(errorLogString(error));
if (error.response?.statusCode == 401 && error.requestOptions.path != 'login' && _retries-- > 0) {
await authService.refreshToken();
var response = await dio.request(
error.requestOptions.path,
data: error.requestOptions.data,
queryParameters: error.requestOptions.queryParameters,
options: Options(
method: error.requestOptions.method,
headers: error.requestOptions.headers,
),
);
return handler.resolve(response);
}
return handler.next(error);
},
I am making POST request using http lib in Dart. After I wandering around former answers with no luck. Even official doc
import 'package:http/http.dart' as http;
Future<http.Response> createAlbum(String nameEN) {
return http.post(
'http://localhost:8000/api/products/',
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'nameEN': nameEN,
}),
);
}
And I test with unittest like this
testWidgets('POST request from offcial docs', (WidgetTester tester) async{
final http.Response res = await createAlbum("Jordan");
print(res.statusCode);
});
Attempt:
PostmanApp can make request and get 201 in response. However, when I copy&paste the Dart code from it and test it does not work
I always get 400 and no request comes to localhost:8000
testWidgets('POST request from postman', (WidgetTester tester) async{
var headers = {
'Content-Type': 'application/json'
};
var request = http.Request('POST', Uri.parse('localhost:8000/api/products/'));
request.body = '''{\n "name_jp": "李天宝",\n "name_en": "Sarit",\n "description": "Developer",\n "qty": 3,\n "expiry": "2099-12-25",\n "barcode": "549XXXYYYYYY",\n "price": "{\\"bounds\\": \\"[)\\", \\"lower\\": \\"12\\", \\"upper\\": \\"6\\"}",\n "medium_price": 60\n}''';
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
print(200);
print(await response.stream.bytesToString());
}
else {
print(response.statusCode);
print(response.reasonPhrase);
}
});
Problem
Dart response returned to me is 400 and on the server side request has not been sent out to my http://localhost:8000/api/products/
Question:
What is the correct POST syntax?
References:
HTTP POST with Json on Body - Flutter/Dart
Bad state: Cannot set the body fields of a Request with content-type "application/json"
Flutter FormatException: Unexpected character (at character 1)
Http POST request with json content-type in dart:io
You have non-ASCII characters. Did you set the proper ; charset=UTF-8 in your Content-Type header? Or even better, JSON doesn't understand non-ASCII unless you \u encode them.
Lesson learnt here
Add CORS enable in chrome
flutter run -d web. Do not use -d chrome
Make request from cors enabled chrome by clicking. Do not use unittest!
I have created a post request in aqueduct dart and it takes json as body parameter, and I need to send that request body to thirdparty api , upon getting response from third party api I need to return that response to user. I have updated the code and printed the response header and it says http 400 (bad request)
here is the code :
#override
Controller get entryPoint {
String dataRecieved;
var completer = new Completer();
var contents = new StringBuffer();
final router = Router();
// Prefer to use `link` instead of `linkFunction`.
// See: https://aqueduct.io/docs/http/request_controller/
router.route("/uploadurl").linkFunction((request) async {
final req = await request.body.decode();
// print( await request.body.decode());
HttpClient client = new HttpClient();
client.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
var auth = 'Bearer ' +
'eyJ...';
await client
.postUrl(Uri.parse(
'https://<removed>/api/datalake/v3/generateDownloadObjectUrls'))
.then((HttpClientRequest requestSend) {
requestSend.headers
.add("Content-Type", "application/json; charset=UTF-8");
requestSend.headers.add("Authorization", auth);
// requestSend.headers.contentLength = request.body.length;
print(req);
requestSend.write(req);
return requestSend.close();
}).then((HttpClientResponse response) async {
print(await response.contentLength);
var resStream = response.transform(Utf8Decoder());
await for (var data in resStream) {
print('Received data: $data');
}
print(await response.statusCode);
}).catchError((e) {
print("Request error: $e"); // The only case
});
print(contents);
return Response.ok({"key": dataRecieved});
});
return router;
}
when I make a request from the postman , I get
{
"key": null
}
I think I am not able to send the correct request to third party API , because when I tested third party API from the postman, it was sending correct response
My pubspec.yaml file is :
name: proxydl
description: An empty Aqueduct application.
version: 0.0.1
author: stable|kernel <jobs#stablekernel.com>
environment:
sdk: ">=2.0.0 <3.0.0"
dependencies:
aqueduct: ^3.0.0
http: ^0.12.0+2
dev_dependencies:
test: ^1.0.0
aqueduct_test: ^1.0.0
This is what I am sending from postman as post request:
{
"paths": [
{
"path": "/f1/f2.log"
}
]
}
This is my first POC with Dart on the server side.
Upon further investigation I found the answer:
final req = await request.body.decode();
var envalue = json.encode(req);
For now, this worked, but I feel there might be a better answer for this
I am trying to get data from API. I need to pass value from the body, in postman without a header: application/JSON data is not displayed.
final response = await http.post(
"http://192.168.10.25:8080/Login/validateusername",
body: {"username": "user#PYA"},
headers: {'Content-Type': 'application/json'},
);
Error Message:
E/flutter (28851): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter (28851): Bad state: Cannot set the body fields of a Request with content-type "application/json".
Add the content type application/json
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;
}
Simply encode body to json object when using content-type "application/json"
http.Response response = await http.post( uri , headers: headers, body: JsonEncoder().convert(body));
Another simple way is as bellow
import 'package:http/http.dart' as http;
String body = json.encode({
'foo': 'bar',
'complex_foo' : {
'name' : 'test'
}
});
http.Response response = await http.post(
url: 'https://example.com',
headers: {"Content-Type": "application/json"},
body: body,
);
use the http dart package
var data = {username:"username",password:"password"};
http.Response response = await http.post(
"yourApiroute",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: {"username": data.phone, "password": data.password});
var json = jsonCodec.encode(data);
print("json=$json");
var url = "http:yourAPIrouter.com/etc";
var response = await http.post(
url,
headers:{ "Accept": "application/json" } ,
body: { "json": '$json'},
encoding: Encoding.getByName("utf-8")
);
and dont forget add the key "json" in postman
I am doing almost the same. However, I tried to avoid doing back-end, like in your case. I just did a minimal php request so that I would not waste or patience learning what is needed to develop a user management controller.
However, I faced several limitations and problems that Flutter alone can't solve. After some denial, I gave a try. Lumen, a light version of the Laravel Framework, some tutorials and some past experience, I eventually realized that the API should carry most of the authentication, and not the application itself. I digressed.
In my case, the code of the fuction to a http post is:
Future<Post> createPost() async {
final url = "http://localhost:8000/api/login";
Map<String, String> body = {
'user': user.text,
'pass': pass.text,
};
await http.post(url, body: body);
print(body);
return http.;
}
I first convert it into a map. I prefer this method over parsing json, because down the line, if I need to add more variables, I just make the map bigger.
I just have a question: What does your http://192.168.10.25:8080/Login/validateusername look like? I think that there is no need to specify the type of information that your body parses.
I am struggling with making a http post call returning JSON in flutter. I keep getting a 500 error and I dont know what the issue is. I need to pass a username and password in the header and I think the issue is how im doing it. Here is the code.
Future<User> LoginUser(String username, String password ) async {
final response =
await http.post('http://xx.xxx.xxx.xxx/api/Login',
headers: {"Content-Type": "application/json", 'email' : username , 'password' : password });
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return User.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load user');
}
}
It wont get past the 200 check because it is getting a 500. I cant find any examples with passing 2 parameters in the header and the content type so im not sure im doing that correctly.
If we have to pass 2 arguments like key and any other data, Then follow the code(Only for Post Request)
Future<ClassType> callApi()async{
const String url="your/request/to/post/link/url";
const Uri=Uri.parse(url);
Map passValues={'token':'yOuRtOkEnkEy','user_id':'123456789'};
var body = json.encode(token);
var response;
try {
response = **await** http.post(
uri,
headers: {'Content-Type': 'application/json'},
body: body,
);
} catch (e) {
print(e);
}
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = json.decode(jsonString);
print(jsonMap);
} else {
print('API FAILED');
}
return response;
}