How to call web services on Rikulo's dart stream server? - dart

I've got a technical problem while trying to consume a restful web service on the Stream server.
I use HTTPClient.openUrl to retrieve a JSON response from another remote server but once the connection is opened, I can no longer write response(connect.response.write) to my browser client.
The error is listed as following:
Unhandled exception:
Bad state: StreamSink is closed
#0 _FutureImpl._scheduleUnhandledError.<anonymous closure> (dart:async:325:9)
#1 Timer.run.<anonymous closure> (dart:async:2251:21)
#2 Timer.run.<anonymous closure> (dart:async:2259:13)
#3 Timer.Timer.<anonymous closure> (dart:async-patch:15:15)
#4 _Timer._createTimerHandler._handleTimeout (dart:io:6730:28)
#5 _Timer._createTimerHandler._handleTimeout (dart:io:6738:7)
#6 _Timer._createTimerHandler.<anonymous closure> (dart:io:6746:23)
#7 _ReceivePortImpl._handleMessage (dart:isolate-patch:81:92)
Any one knows the correct way of calling web services on the stream server?

The key is you have to return Future, since your task (openUrl) is asynchronous. In your sample code, you have to do:
return conn.then((HttpClientRequest request) {
//^-- notice: you must return a future to indicate when the serving is done
For more information, refer to Request Handling. To avoid this kind of mistake, I post a feature request here.
Here is a working sample:
library issues;
import "dart:io";
import "dart:uri";
import "package:rikulo_commons/io.dart" show IOUtil;
import "package:stream/stream.dart";
void main() {
new StreamServer(uriMapping: {
"/": (connect)
=> new HttpClient().getUrl(new Uri("http://google.com"))
.then((req) => req.close())
.then((res) => IOUtil.readAsString(res))
.then((result) {
connect.response.write(result);
})
}).start();
}

Here's my original code:
void startProcess(HttpConnect connect){
String method = "GET";
String url = "http://XXXXXX/activiti-rest/service/deployments";
Map authentication = {"username":"XXXXX", "password":"XXXXX"};
Map body = {"XXXXX":"XXXXX", "XXXXX":"XXXXX"};
processRequest(connect, method, url, authentication, body.toString());
}
void processRequest(HttpConnect connect, String method, String url, Map authentication, String body) {
HttpClient client = new HttpClient();
Uri requestUri = Uri.parse(url);
Future<HttpClientRequest> conn = client.openUrl(method, requestUri);
conn.then((HttpClientRequest request) {
request.headers.add(HttpHeaders.CONTENT_TYPE, Constants.CONTENT_TYPE_JSON);
// Add base64 authentication header, the class is contained in a separate dart file
String base64 = Base64String.encode('${authentication["username"]}:${authentication["password"]}');
base64 = 'Basic $base64';
request.headers.add("Authorization", base64);
switch( method ) {
case Constants.METHOD_GET: break;
case Constants.METHOD_POST:
body = body.replaceAllMapped(new RegExp(r'\b\w+\b'), (match) => '"${match.group(0)}"' );//no replacement for now
request.write(body);
break;
}
return request.close();
})
.then((HttpClientResponse response) {
return IOUtil.readAsString(response);
})
.then((String result) {
connect.response.write(result);
});
}

Related

Send post request in aqueduct dart

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

How to solve Flutter CERTIFICATE_VERIFY_FAILED error while performing a POST request?

I am sending a post request in Dart. It is giving a response when I test it on API testing tools such as Postman. But when I run the app. It gives me the following error:-
E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363))
Here is my code of the function -
Future getAccessToken(String url) async {
try {
http.post('url',
body: {
"email": "xyz#xyz.example",
"password": "1234"
}).then((response) {
print("Reponse status : ${response.statusCode}");
print("Response body : ${response.body}");
var myresponse = jsonDecode(response.body);
String token = myresponse["token"];
});
} catch (e) {
print(e.toString());
}
Here's the full error body:
E/flutter ( 6264): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception: E/flutter ( 6264): HandshakeException: Handshake error in client (OS Error: E/flutter ( 6264): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:363)) E/flutter ( 6264): #0 IOClient.send (package:http/src/io_client.dart:33:23) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:169:38) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #2 BaseClient.post (package:http/src/base_client.dart:54:7) E/flutter ( 6264): #3 post.<anonymous closure> (package:http/http.dart:70:16) E/flutter ( 6264): #4 _withClient (package:http/http.dart:166:20) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #5 post (package:http/http.dart:69:5) E/flutter ( 6264): #6
_MyLoginFormState.getAccessToken (package:chart/main.dart:74:7) E/flutter ( 6264): <asynchronous suspension> E/flutter ( 6264): #7
_MyLoginFormState.build.<anonymous closure> (package:chart/main.dart:64:29)
In order to enable this option globally in your project, here is what you need to do:
In your main.dart file, add or import the following class:
class MyHttpOverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext? context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}
In your main function, add the following line after function definition:
HttpOverrides.global = MyHttpOverrides();
This comment was very helpful to pass through this matter, and please note that...
This should be used while in development mode, do NOT do this when you want to release to production, the aim of this answer is to
make the development a bit easier for you, for production, you need to fix your certificate issue and use it properly, look at the other answers for this as it might be helpful for your case.
Download cert from https://letsencrypt.org/certs/lets-encrypt-r3.pem
Add this file to assets/ca/ Flutter project root directory
Add assets/ca/ assets directory in pubspec.yaml
Add this code on your app initialization:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem');
SecurityContext.defaultContext.setTrustedCertificatesBytes(data.buffer.asUint8List());
runApp(MyApp());
}
It works with the default chain, so no changes are needed on the server.
Android < 7.1.1 clients will still have access in a browser context.
If you are using Dio library, just do this:
Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(HttpClient client) {
client.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
return client;
};
This Code work for me
class MyHttpOverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}
void main(){
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
}
I think it will the same for you...
Edit & Update Feb 2021: When this question was earlier asked there were not enough docs and developers to answer. The following answers may be more helpful than this one:
Ma'moon Al-Akash Answer, Pedro Massango's Answer & Ken's Answer
If you have not found the solution in these 3 answers, you can try the solution below.
Originally Answered Jan 2019:
The correct(but a bad) way to do it, as I found out, is to allow all certificates.
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
String url ='xyz#xyz.example';
Map map = {
"email" : "email" ,
"password" : "password"
};
HttpClientRequest request = await client.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(map)));
HttpClientResponse response = await request.close();
String reply = await response.transform(utf8.decoder).join();
print(reply);
The best approach (I think so) is to allow certificate for trusted hosts, so if your API host is "api.my_app" you can allow certificates from this host only:
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) {
final isValidHost = host == "api.my_app";
// Allowing multiple hosts
// final isValidHost = host == "api.my_app" || host == "my_second_host";
return isValidHost;
});
If you have more hosts you can just add a new check there.
import 'package:http/io_client.dart';
import 'dart:io';
import 'package:http/http.dart';
import 'dart:async';
import 'dart:convert';
Future getAccessToken(String url) async {
try {
final ioc = new HttpClient();
ioc.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
final http = new IOClient(ioc);
http.post('url', body: {"email": "xyz#xyz.example", "password": "1234"}).then(
(response) {
print("Reponse status : ${response.statusCode}");
print("Response body : ${response.body}");
var myresponse = jsonDecode(response.body);
String token = myresponse["token"];
});
} catch (e) {
print(e.toString());
}
}
Check the device date and time in device settings. The device date and time is set to previous date.
This is for http library method. here is what you need to do in order to enable this option globally in your project.
class MyHttpoverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=>true;
}
}
//void main() => runApp(MyApp());
void main(){
HttpOverrides.global=new MyHttpoverrides();
runApp(MyApp());
}
for more details:https://fluttercorner.com/certificate-verify-failed-unable-to-get-local-issuer-certificate-in-flutter/
This issue happened to us as we are not using the fullchain.pem generated using let's encrypt on nginx. Once changed that it fixes this issue.
server {
listen 443 ssl;
ssl_certificate /var/www/letsencrypt/fullchain.pem;
For Apache, you might need to configure SSLCertificateChainFile. More discussion about the issue https://github.com/flutter/flutter/issues/50699
Using Dio package for request on my local server with self signed certificat, i prefer to allow a specific host rather than all domains.
//import 'package:get/get.dart' hide Response; //<-- if you use get package
import 'package:dio/dio.dart';
void main(){
HttpOverrides.global = new MyHttpOverrides();
runApp(MyApp());
}
class MyHttpOverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext context){
return super.createHttpClient(context)
..badCertificateCallback = ((X509Certificate cert, String host, int port) {
final isValidHost = ["192.168.1.67"].contains(host); // <-- allow only hosts in array
return isValidHost;
});
}
}
// more example: https://github.com/flutterchina/dio/tree/master/example
void getHttp() async {
Dio dio = new Dio();
Response response;
response = await dio.get("https://192.168.1.67");
print(response.data);
}
For those who need to ignore certificate errors only for certain calls, you could use the HttpOverrides solution already mentioned by numerous answers.
However, there is no need to use it globally. You can use it only for certain calls that you know experience handshake errors by wrapping the call in HttpOverrides.runWithHttpOverrides().
class IgnoreCertificateErrorOverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext context){
return super.createHttpClient(context)
..badCertificateCallback = ((X509Certificate cert, String host, int port) {
return true;
});
}
}
Future<void> myNonSecurityCriticalApiCall() async {
await HttpOverrides.runWithHttpOverrides(() async {
String url = 'https://api.example.com/non/security/critical/service';
Response response = await get(url);
// ... do something with the response ...
}, IgnoreCertificateErrorOverrides());
}
In my case it is an external API which does have a valid SSL certificate and works in the browser but for some reason won't work in my Flutter app.
Well, I figured out that the actual root of the problem was out-of-sync time on my test device...
For me, it was because I am using HTTPS and the API uses HTTP so I just changed it to HTTP and it works.
For everyone landing here with a need to solve the problem and not just bypass it allowing everything.
For me the problem solved on the server side (as it should be) with no change in the code. Everything is valid now. On all the other solutions the problem still exists (eg The Postman runs but it displays a configuration error on the globe next to response status)
The configuration is Centos/Apache/LetsEncrypt/Python3.8/Django3.1.5/Mod_wsgi/ but I guess that the solution is valid for most installations of Apache/LetsEncrypt
The steps to resolve are
Locate the line "SSLCACertificateFile" on the Virtual Host you wish to config. For example:
SSLCACertificateFile /etc/httpd/conf/ssl.crt/my_ca.crt
Download
https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.txt
At the end of /etc/httpd/conf/ssl.crt/my_ca.crt (after the -----END CERTIFICATE-----) start a new line and paste from lets-encrypt-r3-cross-signed.txt everything bellow -----BEGIN CERTIFICATE----- (including -----BEGIN CERTIFICATE-----)
Save /etc/httpd/conf/ssl.crt/my_ca.crt
Restart Apache httpd
References:
https://access.redhat.com/solutions/43575
https://letsencrypt.org/certs
Also you can check the validity of your cert in https://www.digicert.com/help/.
For me, it was the problem with the android emulator.
I just created a new android emulator that fixed my problem.
Actually in my case I fixed it after updating the date and time on my pc. Might help someone I guess
I specifically needed to use lib/client.dart Client interface for http calls (i.e. http.Client instead of HttpClient) . This was required by ChopperClient (link).
So I could not pass HttpClient from lib/_http/http.dart directly to Chopper.
ChopperClient can receive HttpClient in the constructor wrapped in ioclient.IOClient.
HttpClient webHttpClient = new HttpClient();
webHttpClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
dynamic ioClient = new ioclient.IOClient(webHttpClient);
final chopper = ChopperClient(
baseUrl: "https://example.com",
client: ioClient,
services: [
MfService.create()
],
converter: JsonConverter(),
);
final mfService = MfService.create(chopper);
This way you can temporarily ignore CERTIFICATE_VERIFY_FAILED error in your calls. Remember - that's only for development purposes. Don't use this in production environment!
Update on January 30, 2021:
I know the reason, because nginx is configured with some encryption algorithms that flutter does not support! , The specific need to try.
Use tls 1.3 request URL, no problem.
Example
import 'dart:io';
main() async {
HttpClient client = new HttpClient();
// tls 1.2 error
// var request = await client.getUrl(Uri.parse('https://shop.io.mi-img.com/app/shop/img?id=shop_88f929c5731967cbc8339cfae1f5f0ec.jpeg'));
// tls 1.3 normal
var request = await client.getUrl(Uri.parse('https://ae01.alicdn.com/kf/Ud7cd28ffdf6e475c8dc382380d5d1976o.jpg'));
var response = await request.close();
print(response.headers);
client.close(force: true);
}
This Solution is finally worked. Thanks to Milad
final ioc = new HttpClient();
ioc.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
final http = new IOClient(ioc);
http.post(); //Your Get or Post Request
I fixed the issue by generating the full_chain.crt file.
You might have received the your_domain.crt file and your_domain.ca-bundle file. Now what you have to do is combine the crt file and ca-bundle file to generate the crt file.
cat domain.crt domain.ca-bundle >> your_domain_full_chain.crt
Then you just need to put the your_domain_full_chain.crt file in the nginx and it will start working properly.
In my case I needed to remake my backend's ssl certs. I had generated the certs using mkcert and just had to make new ones.
Note: If this error occurs other than trying to connect to a local SSL please fix it correctly and don't just use badCertificateCallback = (cert, host, port) => true as it is a security risk!
But.. if you run into this issue because you want to connect to a local back-end running a self signed certificate you could do the following.
You can use this client to connect to sources other then you local back-end.
class AppHttpClient {
AppHttpClient({
Dio? client,
}) : client = client ?? Dio() {
if (kDebugMode) {
// this.client.interceptors.add(PrettyDioLogger());
}
}
final Dio client;
}
You can use this client to connect to your local back-end. Make sure you set the --dart-define FLAVOR=development flag when running your app.
class InternalApiHttpClient extends AppHttpClient {
ApiHttpClient({
super.client,
required this.baseUrl,
}) {
_allowBadDevelopmentCertificate();
}
void _allowBadDevelopmentCertificate() {
const flavor = String.fromEnvironment('FLAVOR');
if (flavor == 'development') {
final httpClientAdapter =
super.client.httpClientAdapter as DefaultHttpClientAdapter;
httpClientAdapter.onHttpClientCreate = (client) {
return client..badCertificateCallback = (cert, host, port) => true;
};
}
}
final Uri baseUrl;
}
Doing it this way only suppresses the certificate message when you are on your development environment only when connecting to your local API. Any other requests stay untouched and if failing should be solved in a different way.
If you're using the emulator. So ensure that your date and time are right. Because in my case I found that issue.
If you use Android Emulator I've found out that this occurs when there is no internet connection. Check that your emulator has a network connection!

Flutter - Handle status code 302 in POST request

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

How to catch SocketException using the http library?

This problem has already been pointed out by others (like here). Althought I may have understood the cause, I still haven't found a solution when using the higher-level http library.
For example:
import 'package:http/http.dart';
// yes, pwd is String, it's just a test...
Future<Response> login(String user, String pwd) {
final authHeader = encodeBasicCredentials(user, pwd);
return get(
'http://192.168.0.100:8080/login',
headers: <String, String>{
HttpHeaders.AUTHORIZATION: authHeader,
},
));
}
I can't find a way to catch a SocketException that is thrown, for example, if the host can't be reached (in my case, wrong host ip).
I have tried wrapping the await in try/catch, or using Future.catchError.
This is a stacktrace of the exception:
[ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 4036): SocketException: OS Error: Connection refused, errno = 111, address = 192.168.0.100, port = 35588
E/flutter ( 4036): #0 IOClient.send (package:http/src/io_client.dart:30:23)
E/flutter ( 4036): <asynchronous suspension>
E/flutter ( 4036): #1 BaseClient._sendUnstreamed (package:http/src/base_client.dart:171:38)
E/flutter ( 4036): <asynchronous suspension>
E/flutter ( 4036): #2 BaseClient.get (package:http/src/base_client.dart:34:5)
E/flutter ( 4036): #3 get.<anonymous closure> (package:http/http.dart:47:34)
E/flutter ( 4036): #4 _withClient (package:http/http.dart:167:20)
E/flutter ( 4036): <asynchronous suspension>
E/flutter ( 4036): #5 get (package:http/http.dart:47:3)
You can change login to be async so that you can await the response. That allows you to catch the exception (and, for example, return null instead of the Response).
Future<Response> login(String user, String pwd) async {
final String authHeader = encodeBasicCredentials(user, pwd);
try {
return await get(
'http://192.168.0.100:8080/login',
headers: {
HttpHeaders.AUTHORIZATION: authHeader,
},
);
} catch (e) {
print(e);
return null;
}
}
You can check the type of the exception and treat it accordingly something like:
Future<Response> login(String user, String pwd) async {
final String authHeader = encodeBasicCredentials(user, pwd);
try {
return await get(
'http://192.168.0.100:8080/login',
headers: {
HttpHeaders.AUTHORIZATION: authHeader,
},
);
} catch (e) {
if(e is SocketException){
//treat SocketException
print("Socket exception: ${e.toString()}");
}
else if(e is TimeoutException){
//treat TimeoutException
print("Timeout exception: ${e.toString()}");
}
else print("Unhandled exception: ${e.toString()}");
}
}
Probly better off making an error handler lib, so you can just call a function like handleException(e); on the catch block.
SocketException on http
try {
} on SocketException {
}
One simple way to catch the error is to call a method on the get method by using the catch error argument like so. This method of the get method can catch others types of errors, not only the socket exception. Look at the code below for more
import 'package:http/http.dart';
Future<Response> login(String user, String pwd) {
final authHeader = encodeBasicCredentials(user, pwd);
return get(
'http://192.168.0.100:8080/login',
headers: <String, String>{
HttpHeaders.AUTHORIZATION: authHeader,
},
).catchError(error){
//you can now do your error handling in this block
});
}
}

Completer completeError

I'm trying to caught an error from a completer.
Here, my method to decode a token
Future<Map> decode(String token) {
var completer = new Completer();
new Future(() {
List<String> parts = token.split(".");
Map result = {};
try {
result["header"] = JSON.decode(new String.fromCharCodes(crypto.CryptoUtils.base64StringToBytes(parts[0])));
result["payload"] = JSON.decode(new String.fromCharCodes(crypto.CryptoUtils.base64StringToBytes(parts[1])));
} catch(e) {
completer.completeError("Bad token");
return;
}
encode(result["payload"]).then((v_token) {
if (v_token == token) {
completer.complete(result);
} else {
completer.completeError("Bad signature");
}
});
});
return completer.future;
}
}
The call:
var test = new JsonWebToken("topsecret");
test.encode({"field": "ok"}).then((token) {
print(token);
test.decode("bad.jwt.here")
..then((n_tok) => print(n_tok))
..catchError((e) => print(e));
});
And this is the output
dart server.dart
eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTYifQ==.eyJsdSI6Im9rIn0=.E3TjGiPGSJOIVZFFECJ0OSr0jAWojIfF7MqFNTbFPmI=
Bad token
Unhandled exception:
Uncaught Error: Bad token
#0 _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:820)
#1 _asyncRunCallbackLoop (dart:async/schedule_microtask.dart:41)
#2 _asyncRunCallback (dart:async/schedule_microtask.dart:48)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:126)
I don't understand why we tell me that my error is uncaught while it's printed...
I think you misused .. instead of . for chaining future. See https://www.dartlang.org/docs/tutorials/futures/#handling-errors
instead of
test.decode("bad.jwt.here")
..then((n_tok) => print(n_tok))
..catchError((e) => print(e));
can you try
test.decode("bad.jwt.here")
.then((n_tok) => print(n_tok))
.catchError((e) => print(e));
Have a look at this document about how Futures work - https://www.dartlang.org/articles/futures-and-error-handling/.
In particular there is an example which says:
myFunc()
.then((value) {
doSomethingWith(value);
...
throw("some arbitrary error");
})
.catchError(handleError);
If myFunc()’s Future completes with an error, then()’s Future
completes with that error. The error is also handled by catchError().
Regardless of whether the error originated within myFunc() or within
then(), catchError() successfully handles it.
That is consistent with what you're seeing.

Resources