I am getting blank httpException message while trying to submit client certificate by using Dio package.
DioErrorType (DioErrorType.DEFAULT) : HttpException (HttpException: , uri = [API_PATH])
Same certificate is working when requesting with postman.
HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Thu, 21 Jan 2021 05:56:55 GMT
Below is the code I am using
Future<List<int>> loadCertificate(String assetPath) async {
return (await rootBundle.load(assetPath)).buffer.asUint8List();
}
final List<int> certificateChainBytes =
await loadCertificate('assets/certs/client-cert1.pfx');
final List<int> keyBytes =
await loadCertificate('assets/certs/client-cert1.pfx');
final Dio dio = Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
final SecurityContext sc = SecurityContext()
..useCertificateChainBytes(certificateChainBytes, password: 'Secret')
..usePrivateKeyBytes(keyBytes, password: 'Secret');
final HttpClient httpClient = HttpClient(context: sc);
httpClient.badCertificateCallback =
(X509Certificate cert, String host, int port) {
print('badcertificatecallback');
print(cert.issuer);
return true;
};
return httpClient;
};
try {
url = url.contains('http') ? url : '${PsConfig.ps_app_url}$url';
print('API Response from $url');
final Response response =
await dio.get(url, options: Options(headers: getHeaders()));
return processHttpResponse(obj, response);
} on diolib.DioError catch (e) {
inspect(e);
return processDioResponse(obj, e);
} finally {
dio.close();
}
I am new to this and got stuck. Thank you in advance.
Related
I have response.header in header. I want to pass that header to another response.
so, my approach is to save the header in shared preference and then retrieve it.
final prefs = await SharedPreferences.getInstance();
prefs.setString('header', response.headers.toString());
and then retrieve it
final prefs = await SharedPreferences.getInstance();
String? head = prefs.getString('header');
Map<String, String> authHeaders = jsonDecode(head!);
but authHeders is giving me an error.
Unhandled Exception: FormatException: Unexpected character (at character 2)
header before saving to SharedPreferences.
{connection: keep-alive, set-cookie: XSRF-TOKEN=nBYdTZyY2FrQUl0RitDemxmdnNVMVRacC9XcENyV3pJankiLCJtYWMiOiJkOTRmZWMwYjYyMDdjMjAwNjBmZTJmZDYyNGYzYzI0YTVjMDNmN2VjZjc3MzZkM2UzNzc1MTk2ZjViOGE0MGMyIiwidGFnIjoiIn0%3D; expires=Mon, 01-Aug-2022 08:56:00 GMT; Max-Age=7200; path=/; samesite=lax,laravel_session=eyJpdiI6Iko1U1ZkejhNWVo3b0twb0pwdS9QMVE9PSIsInZhbHVlIjoiVUxyTFVmMm9XSG95VHd6cGxCVThqSExvSzJQWmQ2WWRMRGpoT0xsVjExdHl1M0xnZ2xGZjd2WDRVdHRqa1J4TU4rRjByakFoSUNHT2lPTGVUb0Z4aHNhWjRleTM0TnBjakRPRzc4WkZVWDMvTzdqNlpoVTBCRWRsOTlwcHIvNFQiLCJtYWMiOiI0MTRhMjRkNGY3Njg0ZDI4NjU1MzFmZjI2M2FkOTA5NDc4ZWJhYzhiZTA5MTFkZTQ2ZmJlY2UxNzBlZjc3MTc3IiwidGFnIjoiIn0%3D; path=/; httponly; samesite=lax, cache-control: no-cache, private, date: Mon, 01 Aug 2022 06:56:00 GMT, vary: Accept-Encoding, content-encoding: gzip, cf-cache-status: DYNAMIC, report-to: {"endpoint .......
header after retrieving it.
{connection: keep-alive, set-cookie: XSRF-TOKEN=nBYdTZyY2FrQUl0RitDemxmdnNVMVRacC9XcENyV3pJankiLCJtYWMiOiJkOTRmZWMwYjYyMDdjMjAwNjBmZTJmZDYyNGYzYzI0YTVjMDNmN2VjZjc3MzZkM2UzNzc1MTk2ZjViOGE0MGMyIiwidGFnIjoiIn0%3D; expires=Mon, 01-Aug-2022 08:56:00 GMT; Max-Age=7200; path=/; samesite=lax,laravel_session=eyJpdiI6Iko1U1ZkejhNWVo3b0twb0pwdS9QMVE9PSIsInZhbHVlIjoiVUxyTFVmMm9XSG95VHd6cGxCVThqSExvSzJQWmQ2WWRMRGpoT0xsVjExdHl1M0xnZ2xGZjd2WDRVdHRqa1J4TU4rRjByakFoSUNHT2lPTGVUb0Z4aHNhWjRleTM0TnBjakRPRzc4WkZVWDMvTzdqNlpoVTBCRWRsOTlwcHIvNFQiLCJtYWMiOiI0MTRhMjRkNGY3Njg0ZDI4NjU1MzFmZjI2M2FkOTA5NDc4ZWJhYzhiZTA5MTFkZTQ2ZmJlY2UxNzBlZjc3MTc3IiwidGFnIjoiIn0%3D; path=/; httponly; samesite=lax, cache-control: no-cache, private, date: Mon, 01 Aug 2022 06:56:00 GMT, vary: Accept-Encoding, content-encoding: gzip, cf-cache-status: DYNAMIC, report-to: {"endpoint .......
Yes, both are same.
This is how I am saving header.
login(String phn, String password) async {
final prefs = await SharedPreferences.getInstance();
await getLocation();
String uri = "$baseUrl/User_Login";
try {
http.Response response = await http.post(Uri.parse(uri), headers: {
'Accept': 'application/json'
}, body: {
"number": phn,
"password": password,
"long": long,
"lati": lati
});
print(response.headers);
prefs.setString('header', response.headers.toString());
return json.decode(response.body);
} catch (e) {
return e;
}
}
This is how I am retrieving it and passing it to another response.
getData() async {
final prefs = await SharedPreferences.getInstance();
String? head = prefs.getString('header');
print("headers");
print(head);
Map<String, String> authHeaders = jsonDecode($head!);
// print("authHeaders");
// print(authHeaders.toString());
String uri = "$baseUrlUser/user_bal";
try {
http.Response response =
await http.get(Uri.parse(uri), headers: authHeaders);
return json.decode(response.body);
} catch (e) {
return e;
}
}
The common issue in Flutter when parsing Json data is about the type of request and it's encoding.
Even if you may post the complete source code, at least, the relevant one to your problem, like how you're performing the request, your error probably comes on how you ask for data to the remote.
If you're trying to parse the response as Json, you must specifically add the application/json;charset=UTF-8 in your headers.
But, if you read this line in your source code:
prefs.setString('header', response.headers.toString());
you're saving the headers as string in the header key. This means, that the headers are usually represented in most standars as Map datastructures, so you're asking for retrive as a String in decode(...) a probably bad formatted data.
The solution it's pretty straightfoward. Save the header(s) that your interested one by one, and then retrieve them by key, and the format should be ok to use the jsonDecode.
Edit:
getting directly your code to exemplify as requested by the OP how to get them individually. We will take the Accept header as example, as it's also the unique provide in the example code.
login(String phn, String password) async {
final prefs = await SharedPreferences.getInstance();
await getLocation();
String uri = "$baseUrl/User_Login";
try {
http.Response response = await http.post(Uri.parse(uri), headers: {
'Accept': 'application/json'
}, body: {
"number": phn,
"password": password,
"long": long,
"lati": lati
});
prefs.setString('header', response.headers.get('Accept'));
return json.decode(response.body);
} catch (e) {
return e;
}
}
Then, you only must retrieve it by key:
getData() async {
final prefs = await SharedPreferences.getInstance();
String? head = prefs.getString('Accept');
Map<String, String> authHeaders = new Map();
authHeaders['Accept'] = head!;
String uri = "$baseUrlUser/user_bal";
try {
http.Response response =
await http.get(Uri.parse(uri), headers: authHeaders);
return json.decode(response.body);
} catch (e) {
return e;
}
}
The unique difference is that you're not storing a completly Map as a String as you do in the example, you're saving the keys that you desire as what they are, Strings. Parsing JSON it's alwaya a quite sensible action, so the more granularity over the pieces you take, the best results.
The requirement is to generate a receipt in the form of pdf and send an email to the user using Gmail API. I am able to send attachment but not attachment and message together (multipart). Here is the dart code written for a web app. If I add base64Str - i.e. bytes generated from pdf in the contentMultiPart, it simply creates pdf of base64 string. Hence I am attaching to body externally. Please help with Multipart MIME message.
class AdminCredentials {
final String? email;
final String? accessToken;
final String? idToken;
AdminCredentials(this.email, this.accessToken, {this.idToken});
}
generatepdf() async {
final pdf = pw.Document();
pdf.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (pw.Context context) {
return pw.Center(
child: pw.Text("Hello World"),
); // Center
})); // Page
final bytes = await pdf.save();
return bytes;
}
sendEmail(AdminCredentials? adminCredentials) async {
Map<String, String> header = {};
header['Authorization'] = 'Bearer ${adminCredentials!.accessToken}';
header['Accept'] = 'application/json';
header['Content-type'] = 'application/json';
var pdfbytes = await generatepdf();
final base64Str = base64Encode(pdfbytes);
var from = adminCredentials.email;
var to = "test#gmail.com";
var subject ='Receipt';
var message = '<h1>Receipt generated against your flat </h1>\n<p>Dear Sir/Madam,<br/><br/>Thanks for your payment. Please find attached the receipt generated against your flat <br/><br/>Thanks and regards<br/>Office</p>';
var contentMultiPart = '''
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Type: multipart/mixed; charset="UTF-8"; boundary="part";
to: ${to}
from: ${from}
subject: ${subject}
--part
Content-Type: application/pdf; charset="base64"; Content-Disposition=application; name=receipt.pdf;
--part
Content-Type: text/html; charset="us-ascii";
$message
--part--
''';
List<int> bytes = utf8.encode(contentMultiPart);
String base64 = base64Encode(bytes);
var body = json.encode({'raw': base64+base64Str});
String apiUrl = 'https://www.googleapis.com/gmail/v1/users/' + from! + '/messages/send?uploadType=multipart';
Uri uri = Uri.parse(apiUrl);
final http.Response response = await http.post(uri, headers: header, body: body);
debugPrint('Response send');
print(json.decode(response.body));
if (response.statusCode != 200) {
final Map<String, dynamic> data = json.decode(response.body);
print('error: ' + response.statusCode.toString());
print(data);
return false;
} else {
print('ok: ' + response.statusCode.toString());
print('ok: ' + response.headers.toString());
return true;
}
}
I am trying to fetch data after successful login. I have cookie based auth on backend. I checked the login response and I see that the cookie is present in the headers. I am not sure why my requests are not going with cookie.
I keep getting this in my console, code for not authorized. I/flutter (21293): 401
class Session {
Map<String, String> headers = {};
Future<Map> get(String endpoint) async {
http.Response response = await http.get(Uri.parse("$baseUrl$endpoint"), headers: headers);
print(response.statusCode);
updateCookie(response);
return jsonDecode(response.body);
}
Future<http.Response> post(dynamic data, String endpoint) async {
http.Response response = await http.post(Uri.parse("$baseUrl$endpoint"), body: json.decode(data), headers: headers);
updateCookie(response);
return response;
}
void updateCookie(http.Response response) {
String? rawCookie = response.headers['set-cookie'];
if (rawCookie != null) {
int index = rawCookie.indexOf(';');
headers['cookie'] =
(index == -1) ? rawCookie : rawCookie.substring(0, index);
}
}
}
Tried to print rawCookie as well
I/flutter (21293): _api_key=SFMyNTY.g3QAAAABbQAAAAhpZGVudGl0eXQAAAAHZAAKX19zdHJ1Y3RfX2QAF0VsaXhpci5HYXRld2F5LklkZW50aXR5ZAAGYWN0aXZlZAAEdHJ1ZWQACWF2YXRhcl9pZG0AAAAkNTY4NWMwNTMtYThhMS00MDA5LWJhN2UtZmJkNTkyMjBhM2U1ZAAHY291bnRyeXQAAAAGZAAEZmxhZ20AAAAI8J-HtfCfh7FkAAppc29fbmFtZV8ybQAAAAJwbGQACmlzb19uYW1lXzNtAAAAA3BvbGQABG5hbWVtAAAABlBvbGFuZGQACG51bV9jb2RlbQAAAAM2MTZkAAVwb2ludHQAAAAEZAAKX19zdHJ1Y3RfX2QAEEVsaXhpci5HZW8uUG9pbnRkAAtjb29yZGluYXRlc2gCYRRhNGQACnByb3BlcnRpZXN0AAAAAGQABHNyaWRiAAAQ5mQAC2Rlc2NyaXB0aW9ubQAAAAF4ZAACaWRtAAAAJGM2ZTljY2Q0LTM4MmItNDEzZi04ODYyLTc2ZjM5ZTYxOGFiNGQABG5pY2ttAAAACHRlc3Rzc3Nz.80iQK3sUwPPVj1pkaZsKgMxQ4Lt8aW8-ndYbPSucGag; path=/; HttpOnly
I/flutter (21293): 200
Then I use it
class Items{
Future<Map> fetchItems() async {
final response = await Session().get("/user/items");
return response;
}
}
I had the same problem. I resolved it by using dio and cookiejar instead of HTTP.
Add these dependencies in your pubspec.yaml:
dependencies:
dio: ^4.0.4
dio_cookie_manager: ^2.0.0
cookie_jar: ^3.0.1
Here's an example to use dio:
var dio = Dio(BaseOptions(
connectTimeout: 10000, // in ms
receiveTimeout: 10000,
sendTimeout: 10000,
responseType: ResponseType.plain,
followRedirects: false,
validateStatus: (status) { return true; }
)); // some dio configurations
dio.interceptors.add(CookieManager(CookieJar()));
Response response = await dio.post(
"http://example.com/login",
data: FormData.fromMap(
{
'username': 'myUser',
'password': 'myPassword',
}
)); // cookies are automatically saved
Response nextResponse = await dio.post("http://example.com/user/items");
I tried to upload a file to s3 using MultipartRequest in flutter but upon reach "response.send()" i get
I/flutter ( 8307): HandshakeException: Handshake error in client (OS Error:
I/flutter ( 8307): CERTIFICATE_VERIFY_FAILED: self signed certificate in certificate chain(handshake.cc:354))
I turned 'SSL Certificate Verification' off in Postman for it work, so is there any way to turn it off in flutter whilst uploading?
here is the code i used:
var request = http.MultipartRequest('POST', uri)
..fields['key'] = data.key
..fields['x-amz-algorithm'] = data.algorithm
..fields['x-amz-credential'] = data.credential
..fields['x-amz-date'] = data.date
..fields['x-amz-security-token'] = data.securityToken
..fields['policy'] = data.policy
..files.add(await http.MultipartFile.fromPath('File', imagePath, filename: imageName));
print(request.toString());
try {
var response = await request.send();
await for (var value in response.stream.transform(utf8.decoder)) {
print(value);
}
} catch (e) {
print(e.toString());
}
If anyone faces the same, I was able to fix the above issue using "Dio" package like so
Dio _client = Dio();
_client.interceptors.add(LogInterceptor());
FormData formData = FormData.fromMap({
'key': data.key,
'x-amz-algorithm': data.algorithm,
'x-amz-credential': data.credential,
'x-amz-date': data.date,
'x-amz-security-token': data.securityToken,
'policy': data.policy,
'x-amz-signature': data.signature,
'File': await MultipartFile.fromFile(
filePath,
filename: fileName,
)
});
(_client.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(HttpClient dioClient) {
dioClient.badCertificateCallback =
(X509Certificate cert, String host, int port) => true;
return dio;
};
try {
await _client.post(
data.uploadUrl,
data: formData,
);
_client.close();
} catch (e) {
print(e.toString());
_client.close();
}
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
String url ='https://dev.jobma.com:8090/v4/jobseeker/login';
Map map = {
"email":"hope#yopmail.com",
"password":"123456"
};
print(map);
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);
and response from server showing this
{"error": 1, "data": {}, "message": "Please add mandatory fields: email, password"}
It is much easier if you can use http package available in dart pub.
import 'package:http/http.dart' as http;
String url = 'https://dev.jobma.com:8090/v4/jobseeker/login';
Map map = {
"email": "hope#yopmail.com",
"password": "123456"
};
var response = await http.post(url, body: map);
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
It seems that without the "content-length" headers, that server isn't accepting the request properly. This will make your code work:
HttpClient client = new HttpClient();
client.badCertificateCallback =
((X509Certificate cert, String host, int port) => true);
String url = 'https://dev.jobma.com:8090/v4/jobseeker/login';
Map map = {
"email": "hope#yopmail.com",
"password": "123456"
};
print(map);
// Creating body here
List<int> body = utf8.encode(json.encode(map));
HttpClientRequest request = await client.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
// Setting the content-length header here
request.headers.set('Content-Length', body.length.toString());
// Adding the body to the request
request.add(body);
HttpClientResponse response = await request.close();
String reply = await response.transform(utf8.decoder).join();
print(reply);