How to get device user agent information in Flutter - dart

I'm building a flutter app which needs to send user agent information along with the http request. I'm using http dart package to send requests. How to get user agent string in flutter and use it with http package?

I done it by calling native methods in flutter. First you have to add method channel in android Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getUserAgent")) {
result.success(System.getProperty("http.agent"));
} else {
result.notImplemented();
}
}
});
}
Then getUserAgent() method can be called in flutter like below
Future<String> _getUserAgent() async {
try {
return await platform.invokeMethod('getUserAgent');
} catch (e) {
return 'Unknown';
}
}

You can get it in a cross-platform way by using the flutter_user_agent library: https://pub.dev/packages/flutter_user_agent.
import 'package:flutter_user_agent/flutter_user_agent.dart';
...
String ua = await FlutterUserAgent.getPropertyAsync('userAgent');

this worked for me by using flutter_user_agent library: https://pub.dev/packages/flutter_user_agent
as mentioned above
String _userAgent = await FlutterUserAgent.getPropertyAsync('userAgent');
final _response = await http.get(_url, headers: {
'Content-Type': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': '${_userAgent.toLowerCase()}',
});

Found a library that does it. It would be interesting to look at what the library does, i don't think it's needed to implement a library for that.
https://pub.dartlang.org/packages/user_agent
An example on how you would use it:
main() async {
app.get('/', (req, res) async {
var ua = new UserAgent(req.headers.value('user-agent'));
if (ua.isChrome) {
res.redirect('/upgrade-your-browser');
return;
} else {
// ...
}
});
}
Alternatively, if you want to add a user-agent to the http client, you can do it this way:
Future<http.Response> fetchPost() {
return http.get(your_url,
// Send user-agent header to your backend
headers: {HttpHeaders.userAgentHeader: "your_user_agent"},
);
}
You can look at HttpHeadersto see the full list of predefined headers, although headers take a map, you could create your own header if you want.

Related

Dart server side: How to receive data from the Postman (form-data)?

I am using dart:io to create the server. I send the request from the Postman with form-data. I need to use form-data because my old API from another language uses it and the app uses it too.
At the moment. I am trying to get the data and files with this code:
Future main(List<String> arguments) async {
HttpServer server = await HttpServer.bind('localhost', 8085);
server.listen((HttpRequest request) async {
String jsonString = await request.cast<List<int>>().transform(utf8.decoder).join();
print("jsonString:\n$jsonString");
await request.response.close();
});
}
When I send the data and a file from the Postman with this below.
I will get the error below.
Unhandled exception:
FormatException: Unexpected extension byte (at offset 435)
If I don't send the file as image 1, I got this.
jsonString:
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 1"
Content-Type: application/json
value 1
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 2"
value 2
----------------------------166099235909119466948633--
I can't convert the above results to variables.
I don't know how to do that. Has anyone an example for doing this or suggest any package to me? This is my first time creating a dart server.
I follow this.
You can get the data and files from the request by using shelf_multipart (Other packages may be used in conjunction with this one and find more methods on GitHub).
If you want to see results quickly that it can be done. Follow this below.
I am using 3 packages including the shelf, shelf_router, and shelf_multipart packages.
You need to add these packages to your pubspec.yaml.
(You can copy and paste these into your pubspec.yaml.)
dependencies:
shelf: ^1.4.0
shelf_router: ^1.1.3
shelf_multipart: ^1.0.0
Then copy my code and past it to your main.dart:
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_multipart/form_data.dart';
import 'package:shelf_multipart/multipart.dart';
Future main(List<String> arguments) async {
final service = Service();
final server = await shelf_io.serve(service.handler, 'localhost', 8085);
print('Server running on localhost:${server.port}');
}
class Service {
Handler get handler {
final router = Router();
router.post("/example", (Request request) async {
if (request.isMultipart && request.isMultipartForm) {
Map<String, dynamic>? data = await RequestConverter.formData(request);
return data != null
? Response.ok("form-data: true, receive-data: true, data: $data")
: Response.ok("form-data: true, receive-data: false");
}
return Response.ok("form-data: false");
});
router.all('/<ignored|.*>', (Request request) {
return Response.notFound('Page not found');
});
return router;
}
}
class RequestConverter {
static Future<Map<String, dynamic>?> formData(Request request) async {
try {
Map<String, dynamic> data = {};
Map<String, dynamic> files = {};
final List<FormData> formDataList = await request.multipartFormData.toList();
for (FormData formData in formDataList) {
if (formData.filename == null) {
String dataString = await formData.part.readString();
data[formData.name] = Json.tryDecode(dataString) ?? dataString; //Postman doesn't send data as json
} else if (formData.filename is String) {
files[formData.name] = await formData.part.readBytes();
}
}
return {"data": data, "files": files};
} catch (e) {
return null;
}
}
}
class Json {
static String? tryEncode(data) {
try {
return jsonEncode(data);
} catch (e) {
return null;
}
}
static dynamic tryDecode(data) {
try {
return jsonDecode(data);
} catch (e) {
return null;
}
}
}
After this, you can start your server in your terminal. For me I am using:
dart run .\bin\main.dart
Finally, open the Postman and paste http://localhost:8085/example to the URL field, select the POST method, and form-data. You can add the data into the KEY and VALUE fields. Then press send.
This is my example in the Postman:
This solution work with http.MultipartRequest() from the Flutter app.

How to Implement Flutter Web SSL Certificate (SSL Pinning)

I am building a flutter web app and I need to use SSL to talk to the server using a .pem certificate.
I am using HttpClient and IOClient to get it to work and the code for this looks as following:
fetchData()async{
HttpClient _client = HttpClient(context: await globalContext);
_client.badCertificateCallback =
(X509Certificate cert, String host, int port) => false;
IOClient _ioClient = new IOClient(_client);
var response = await _ioClient.get(Uri.parse('https://appapi2.test.bankid.com/rp/v5.1'));
print(response.body);
}
Future<SecurityContext> get globalContext async {
final sslCert1 = await
rootBundle.load('assets/certificates/bankid/cert.pem');
SecurityContext sc = new SecurityContext(withTrustedRoots: false);
sc.setTrustedCertificatesBytes(sslCert1.buffer.asInt8List());
return sc;
}
I get the following error when trying to run fetchData:
Unsupported operation: SecurityContext constructor
I have also tried using the flutter plugin DIO that looks like this:
void bid() async {
final dio = Dio();
ByteData bytes = await rootBundle
.load('assets/certificates/bankid/FPTestcert4_20220818.pem');
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
SecurityContext sc = SecurityContext();
sc.setTrustedCertificatesBytes(bytes.buffer.asUint8List());
HttpClient httpClient = HttpClient(context: sc);
return httpClient;
};
try {
var response = await dio.get('https://appapi2.test.bankid.com/rp/v5.1');
print(response.data);
} catch (error) {
if (error is DioError) {
print(error.toString());
} else {
print('Unexpected Error');
}
}
}
When running this I get the following error:
Error: Expected a value of type 'DefaultHttpClientAdapter', but got one of type
'BrowserHttpClientAdapter'
I understand that I get the error above because of the casting that the httpClientAdapter is used as a DefaultHttpClientAdapter but since the app is running in the browser its using BrowserHttpClientAdapter, but how do I solve this?
Is it possible to make this work?

Is there any way to track an event using firebase in electron + react

I want to ask about how to send an event using firebase & electron.js. A friend of mine has a problem when using firebase analytics and electron that it seems the electron doesn't send any event to the debugger console. When I see the network it seems the function doesn't send anything but the text successfully go in console. can someone help me to figure it? any workaround way will do, since he said he try to implement the solution in this topic
firebase-analytics-log-event-not-working-in-production-build-of-electron
electron-google-analytics
this is the error I got when Try to use A solution in Point 2
For information, my friend used this for the boiler plate electron-react-boilerplate
The solution above still failed. Can someone help me to solve this?
EDIT 1:
As you can see in the image above, the first image is my friend's code when you run it, it will give a very basic example like in the image 2 with a button to send an event.
ah just for information He used this firebase package :
https://www.npmjs.com/package/firebase
You can intercept HTTP protocol and handle your static content though the provided methods, it would allow you to use http:// protocol for the content URLs. What should make Firebase Analytics work as provided in the first question.
References
Protocol interception documentation.
Example
This is an example of how you can serve local app as loaded by HTTP protocol and simulate regular browser work to use http protocol with bundled web application. This will allow you to add Firebase Analytics. It supports poorly HTTP data upload, but you can do it on your own depending on the goals.
index.js
const {app, BrowserWindow, protocol} = require('electron')
const http = require('http')
const {createReadStream, promises: fs} = require('fs')
const path = require('path')
const {PassThrough} = require('stream')
const mime = require('mime')
const MY_HOST = 'somehostname.example'
app.whenReady()
.then(async () => {
await protocol.interceptStreamProtocol('http', (request, callback) => {
const url = new URL(request.url)
const {hostname} = url
const isLocal = hostname === MY_HOST
if (isLocal) {
serveLocalSite({...request, url}, callback)
}
else {
serveRegularSite({...request, url}, callback)
}
})
const win = new BrowserWindow()
win.loadURL(`http://${MY_HOST}/index.html`)
})
.catch((error) => {
console.error(error)
app.exit(1)
})
async function serveLocalSite(request, callback) {
try {
const {pathname} = request.url
const filepath = path.join(__dirname, path.resolve('/', pathname))
const stat = await fs.stat(filepath)
if (stat.isFile() !== true) {
throw new Error('Not a file')
}
callback(
createResponse(
200,
{
'content-type': mime.getType(path.extname(pathname)),
'content-length': stat.size,
},
createReadStream(filepath)
)
)
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function serveRegularSite(request, callback) {
try {
console.log(request)
const req = http.request({
url: request.url,
host: request.url.host,
port: request.url.port,
method: request.method,
headers: request.headers,
})
if (req.uploadData) {
req.write(request.uploadData.bytes)
}
req.on('error', (error) => {
callback(
errorResponse(error)
)
})
req.on('response', (res) => {
console.log(res.statusCode, res.headers)
callback(
createResponse(
res.statusCode,
res.headers,
res,
)
)
})
req.end()
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function toStream(body) {
const stream = new PassThrough()
stream.write(body)
stream.end()
return stream
}
function errorResponse(error) {
return createResponse(
500,
{
'content-type': 'text/plain;charset=utf8',
},
error.stack
)
}
function createResponse(statusCode, headers, body) {
if ('content-length' in headers === false) {
headers['content-length'] = Buffer.byteLength(body)
}
return {
statusCode,
headers,
data: typeof body === 'object' ? body : toStream(body),
}
}
MY_HOST is any non-existent host (like something.example) or host that is controlled by admin (in my case it could be electron-app.rumk.in). This host will serve as replacement for localhost.
index.html
<html>
<body>
Hello
</body>
</html>

How to log http requests in flutter

I am developing an app with flutter and I am using default http package in dart for making API calls. How do we log all the http requests which are going through. Is there any in built feature in http or middleware available for the same?
There doesn't seem to be a built-in way to log request. However, you can implement your own Client to log request:
class MyClient extends BaseClient {
MyClient(this.delegate);
final Client delegate;
Future<StreamedResponse> send(BaseRequest request) {
_logRequest(request);
return delegate.send(request);
}
void close() => delegate.close();
void _logRequest(BaseRequest request) => ....;
}
Just debugging solution as is
class LoggableHttpClient extends BaseClient {
final Client _delegate;
final Logger _logger;
LoggableHttpClient(this._delegate, this._logger);
#override
void close() {
_delegate.close();
}
#override
Future<StreamedResponse> send(BaseRequest request) async {
String s = "${request.method} ${request.url} -->";
s += "\nheader: ${request.headers}";
if(request is Request && request.body.length>0) {
s += "\nbody: ${request.body}";
}
_logger.info(s);
final response = await _delegate.send(request);
s = "${request.method} ${request.url} <--";
s += "\nheader: ${response.headers}";
// Simple request
if(request is Request) {
final List<int> bytes = await response.stream.toBytes();
s += "\nbody: ${await utf8.decode(bytes)}";
_logger.info(s);
return StreamedResponse(
ByteStream.fromBytes(bytes),
response.statusCode,
contentLength: response.contentLength,
request: request,
headers: response.headers,
isRedirect: response.isRedirect,
persistentConnection: response.persistentConnection,
reasonPhrase: response.reasonPhrase
);
}
_logger.info(s);
return response;
}
}
You can user http_logger
Add them to you pubspec.yaml like this
http: ^0.11.3+16
http_middleware: ^1.0.0
http_logger: ^1.0.0
Note that: http_logger 1.0.0 only works with http 0.11.3+16. (update 02/04/2020).
And import them to file like this:
import 'package:http_middleware/http_middleware.dart';
import 'package:http_logger/http_logger.dart';
import 'package:http/http.dart' as http;
And use them :
HttpWithMiddleware httpClient = HttpWithMiddleware.build(middlewares: [
HttpLogger(logLevel: LogLevel.BODY),
]);
final http.Response response = await httpClient.post(
"https:nhatvm.com/v1/user/login",
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{'email': email, 'password': password}),
);
You can use requests_inspector package.
void main() {
runApp(const RequestsInspector(
enabled: true,
child: MyApp(),
));
}
Screenshots
Note: Don't forget to add the request using RequestsInspectorInterceptor or using InspectorController.addRequest().
You can user pretty_http_logger Add it to your pubspec.YAML like this
pretty_http_logger: ^0.2.1
And use it like this:
HttpWithMiddleware http = HttpWithMiddleware.build(middlewares: [
HttpLogger(logLevel: LogLevel.BODY),
]);
That is it! Now go ahead and use this http object as you would normally do.
Simple POST request
var response = await http.post('https://jsonplaceholder.typicode.com/posts/',
body: {"testing", "1234"});
Simple GET request
var response = await http.get('https://jsonplaceholder.typicode.com/posts/');
It will print out all the headers, request body, response, and error in a proper format that is easy to read and looks pretty.

send verification mail in flutter/dart development

I'm New to flutter and dart mobile app development. how to implement forgot password and send verification mail in flutter/dart development or is there any way to implement to send mail.
I don't think there is any way to send an email from your flutter application. This is something I would definitely implement on a backend server.
I would implement a 'forgot password' button in flutter, which triggers a http call to the backend which then triggers the password generation and email sending.
Yes there are some ways. The most common would be to use firebase as a backend server handling these requests.
You could do it like this
Add these packages to your flutter apps pubspec.yaml file
// latest version
firebase_core: ^1.17.0
firebase_auth: ^3.3.18
On forgot password button click
once you've completed the logic necessary before making the request, call this function
sendResetEmail(String email, BuildContext context) async {
final FirebaseAuth _auth = FirebaseAuth.instance;
try {
await _auth.sendPasswordResetEmail(email: email);
Timer(
const Duration(seconds: 3),
() => CustomWidgets().moveToPage(
page: const Home(), context: context, replacement: true),
);
} catch (e) {
// error handling here
}
}
This will send an email from firebase to the selected email to reset password.
On email verification request
Once the logic is over, call this function.
bool _isEmailVerified = false;
Timer? timer;
final FirebaseAuth _auth = FirebaseAuth.instance;
the initstate method
#override
void initState() {
_isEmailVerified = _auth.currentUser!.emailVerified;
if (!_isEmailVerified) {
sendEmailVerificationForUser();
timer = Timer.periodic(const Duration(seconds: 5), (timer) {
emailVerificationStatus(context);
});
}
email verification check function
emailVerificationStatus(BuildContext context) async {
try {
await _auth.currentUser!.reload();
setState(() {
_isEmailVerified = _auth.currentUser!.emailVerified;
});
} catch (e) {
// handle the error here
}
setState(() {
_isEmailVerified = _auth.currentUser!.emailVerified;
});
if (_isEmailVerified) {
timer?.cancel();
// and move to next page
}}
send email verification function
Future sendEmailVerificationForUser() async {
try {
await FirebaseAuth.instance.currentUser!.sendEmailVerification();
} catch (e) {
// error handling}

Resources