how to pass parameters in http get request? - flutter

i would like to pass parameters in a http get request in my flutter app. here is the code cUrl code i want to transform in a http get request.
and here is the code i want to ameliorate
Future<http.Response> getTarget(String type, String q) async {
try {
final response = await dio.Dio().get(
'https://graph.facebook.com/v2.11/search',
queryParameters: {'type': type, 'q': q, 'access_token': ACCESS_TOKEN},
);
} catch (e) {
print('Error: $e');
print(e.code);
}
}

You can checkout the following link (see examples section).
As per your question, you can send the parameters in queryParameters (as you already did) or you can pass them in the URL as in the following:
Future<http.Response> getTarget(String type, String q) async {
try {
final response = await dio.Dio().get(
"https://graph.facebook.com/v2.11/search?type=${type}&q=${q}&access_token=${ACCESS_TOKEN}"
);
} catch (e) {
print('Error: $e');
print(e.code);
}
}

Related

http put did not send any response in flutter

Hey I have this app where I can update status, and I use http.put method, but it takes forever to response. I got this error
error
And here is the code for http.put
Future<void> mapEventToState(
Emitter<ReportStatusState> emit, ReportStatusEvent event) async {
emit(ReportStatusLoading());
ReportStatusPut statusPut = event.statusPutBody;
// ReportStatusModel model =
// await apiAuthRepository.updateReportStatus(statusPut, event.id);
ReportStatusModel model = await updateReportStatus({'biaya': '0', 'status': 'SELESAI'}, event.id);
print(model);
if (!model.success) {
emit(ReportStatusFailure(model.message));
}
print(model.code);
emit(ReportStatusSuccess());
}}
Future<ReportStatusModel> updateReportStatus(
Map data, String id) async {
final SharedPreferencesManager sharedPreferencesManager =
locator<SharedPreferencesManager>();
String? token =
sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken);
try {
final response = await http.put(
Uri.parse('https://api.komplekku.com/officer/api/report/v1/$id'),
body: json.encode(data),
headers: {'Authorization': 'Bearer $token'});
return ReportStatusModel.fromJson(json.decode(response.body));
} catch (e) {
throw Exception(e);
}
}
There is nothing wrong with the API, I already check using Postman and it worked perfectly fine, Anyone know what went wrong?

Flutter how can i set Auth token from flutter secure storage to dio header?

After login i setting user token to my user Secure storage. Like :
Future<AuthResponseModel?> login(AuthRequstModel model) async {
try {
Response response = await _dio.post(loginPath, data: model);
if (response.statusCode == 200) {
final AuthResponseModel authResponseModel = AuthResponseModel.fromJson(response.data);
if (authResponseModel.success!) {
await UserSecureStorage.setField("token", authResponseModel.token);
}
return AuthResponseModel.fromJson(response.data);
}
return null;
} catch (e) {
return null;
}
}
User Secure Storage =>
class UserSecureStorage {
static const _storage = FlutterSecureStorage();
static Future setField(String key, value) async {
await _storage.write(key: key, value: value);
}
static Future<String?> getField(key) async {
return await _storage.read(key: key);
}
But problem is when i want to make apiservice and when i want to auth token inside header of dio, I cant access it becouse its a future<String?> function. But i cant use await coz its inside of baseoption. Like :
class ApiService {
final _dio = Dio(BaseOptions(headers: {
'authorization': 'Bearer ${UserSecureStorage.getField("token")}', //I cant access here its only giving instance.
}));
Future<Response?> get(String path) async {
try {
Response response = await _dio.get('${ApiConstants.BASE_URL}$path');
if (response.statusCode == 200) {
return response;
}
return null;
} on DioError catch (e) {
return null;
}
}
What can i do for solve that problem ? I tried use .then(value=>value) after tried get token but didnt work too. Thanks for responses!
I think token is not getting updated because _dio is already intitalized.
Try to request for token when dio request is made like :
class ApiService {
final _dio = Dio();
Future<Response?> get(String path) async {
try {
Response response = await _dio.get('${ApiConstants.BASE_URL}$path', options: Options(headers: {"authorization": "Bearer ${UserSecureStorage.getField("token")}"}));
if (response.statusCode == 200) {
return response;
}
return null;
} on DioError catch (e) {
return null;
}
}
Use options in get method to add headers for a single request or interceptors for all requests.
I think that it is not an issue easily solvable, I would try with two different methods, you can maintain the token in a state manager such as Provider so you don't have to rely on an async function to retrive it, but this of course add in the code the state manager structure that complicates thing a little.
A bit more naive way to solve this could be to include a async initializator in the ApiService class such this
class ApiService {
late final _dio;
Future<void> init() async {
_dio = Dio(BaseOptions(headers: {
'authorization': 'Bearer ${UserSecureStorage.getField("token")}', //I cant access here its only giving instance.
}));}
Future<Response?> get(String path) async {
try {
Response response = await _dio.get('${ApiConstants.BASE_URL}$path');
if (response.statusCode == 200) {
return response;
}
return null;
} on DioError catch (e) {
return null;
}
}
And this introduce us a new issue, we have to call init everytime the class ApiService is instantiated, to solve this you could use the package get_it which grants you the possibility to instatiate only once the class and access it from everywhere in your project.
I hope this will help you solve your problem
your are getting instance because UserSecureStorage.getField("token") is future so you can get token when you put await keyword
so try like this
await UserSecureStorage.getField("token")

Dio instance doesnt return a response when getting a API endpoint

I am trying to get a response from an Api but there is no response in return.
Dio instance doesnt return a response when getting a API endpoint
import 'package:dio/dio.dart';
class DioService {
static const baseUrl = "https://dictionaryapi.dev/";
Dio dio = Dio();
DioService() {
initializeInterceptors();
}
initializeInterceptors() {
dio.interceptors.add(InterceptorsWrapper(onError: (e, _) {
print(e.message);
}, onRequest: (r, _) {
print(r.method);
print(r.path);
}, onResponse: (r, _) {
print(r.data);
}));
}
Future<Response> getRequest(String endPoint) async {
Response response;
try {
response = await dio.get(endPoint);
print(response.toString());
} on DioError catch (e) {
print(e);
throw Exception(e.message);
}
return response;
}
}
console :
I/flutter (17735): GET
I/flutter (17735): https://api.dictionaryapi.dev/api/v2/entries/en_US/dog
Your interceptor is routing the request into a black hole.
The second parameter to each interceptor method is a handler. In order to continue a request, the interceptor must pass (possibly modified) request options to the handler, and the same holds for the error and response handlers.
dio.interceptors.add(InterceptorsWrapper(onError: (e, handler) {
print(e.message);
handler.next(e);
}, onRequest: (r, handler) {
print(r.method);
print(r.path);
handler.next(r);
}, onResponse: (r, handler) {
print(r.data);
handler.next(r);
}));
For logging in particular, note that Dio provides a LogInterceptor class, so you don't need to roll your own.

How to put this dart class together so I can use it in different parts of my code

I am trying to implement a Dio interceptor so I will be able to use it with my code.
I will be using this interceptor in many places. So I thought it would make sense to put it into a class or an interface whichever is best and just extend or implement it my subclasses.
I have this so far:
class AppInterceptor {
Dio dio = Dio();
AppInterceptor() {
dio.interceptors
.add(InterceptorsWrapper(onRequest: (Options options) async {
var token = await getAuthorizationToken();
options.headers["Authorization"] = 'Bearer $token';
return options;
}, onResponse: (Response response) {
// Finally, the string is parsed into a JSON object.
//print(response.toString());
return response;
}, onError: (DioError e) {
print('somthing went wrong');
// Do something with response error
return e; //continue
}));
}
}
How do I use this in a subclass to make an http call?
I tried something like this when trying to do my http call:
Response response = await AppInterceptor.dio.post(Global.functionsUrl+'/auth/linkuseraccount/', data: {'hey': 'hello'});
print(response);
It fails each time with Unhandled Exception: DioError [DioErrorType.RESPONSE]: Http status error [403]
From my backend, I can tell that it fails because the interceptor didn't pass in the authentication header.
How should I go about this?

How do I call a method as a parameter from a subclass in the Object class in dart?

I have a method inside a service class:
#override
Future<String> registerNewVoter(Object deviceAppInfo) async {
Dio dio = new Dio();
final url = API().endpointVoterUri(EndpointVoter.newVoter).toString();
final header = {'Content-type': 'application/json'};
final data = await deviceAppInfo; ///need to call the method getInfo() on the Object class which returns a future
final response =
await dio.post(url, data: data, options: Options(headers: header));
if (response.statusCode == 200) {
Map map = response.data;
final uuid = map['result']['voter_uuid'];
return uuid;
}
print(
'Request $url failed\nResponse: ${response.statusCode} ${response.statusMessage}');
throw response;
}
I'm using type Object deviceAppInfo as a parameter in the method to keep the service as pure as possible(adhering to mvvm principles). The subclass is DeviceAppInfo which has an async method called getInfo()(and where the data comes from) which is supposed to be assigned to data(see the comments in the code). I'm struggling to see how I can keep the class decoupled from DeviceAppInfo class. Any suggestions...? I'm thinking of calling a factory constructor but not sure how to implement it. Here is my DeviceAppInfo class:
class DeviceAppInfo {
DeviceAppInfo({
this.platform,
this.platformVersion,
this.appVersion,
});
final String platform;
final String platformVersion;
final String appVersion;
Map<String, dynamic> toMap() => {
'platform': this.platform,
'platform_version': this.platformVersion,
'app_version': this.appVersion,
};
Future<Map<String, dynamic>> getInfo() async {
final values = await Future.wait([
getPlatform(),
getPlatformVersion(),
getProjectVersion(),
]);
return DeviceAppInfo(
platform: values[0],
platformVersion: values[1],
appVersion: values[2],
).toMap();
}
Future<String> getPlatform() async {
try {
if (Platform.isIOS) {
return 'ios';
}
return 'android';
} on PlatformException catch (e) {
return e.toString();
}
}
Future<String> getPlatformVersion() async {
try {
final platformVersion = await GetVersion.platformVersion;
return platformVersion;
} on PlatformException catch (e) {
return e.toString();
}
}
Future<String> getProjectVersion() async {
try {
final projectVersion = await GetVersion.projectVersion;
return projectVersion;
} on PlatformException catch (e) {
return e.toString();
}
}
}
I believe that DeviceAppInfo is a clear collaborator of your service, and hiding it behind Object is simply bad engineering:
it will make your Api hard to use correctly and easy to use incorrectly
Your api is no longer self-documenting, without reading the docs or code it is impossible to use it correctly.
However, it can be discussed if it should be exposed as a parameter or provided to the constructor of your service.
Having said that, There are at least 3 options that will decouple your service from DeviceAppInfo:
Option 1: Pass in the result of getInfo() to your method
least questionable and a common form of decoupling inbound data
I am a bit sceptical if you use a Map as an input type, it is still easy to provide a map with incorrect keys
Option 2: take a function as an argument
Function a bit harder to use, it is not evident what functions accross the codebase can be used (compared to a class)
Option 3: cast to dynamic
Please dont do that
Most closely matches your goal from question
function is extremely hard to use correctly Without reading docs / code
You change compile-time errors to runtime errors
Is this what you want?
#override
Future<String> registerNewVoter(DeviceAppInfo deviceAppInfo) async {
Dio dio = new Dio();
final url = API().endpointVoterUri(EndpointVoter.newVoter).toString();
final header = {'Content-type': 'application/json'};
final data = await deviceAppInfo.getInfo();
final response =
await dio.post(url, data: data, options: Options(headers: header));
if (response.statusCode == 200) {
Map map = response.data;
final uuid = map['result']['voter_uuid'];
return uuid;
}
print(
'Request $url failed\nResponse: ${response.statusCode} ${response.statusMessage}');
throw response;
}
NOTE: I just changed the type of deviceAppInfo from Object to DeviceAppInfo