How to set base url to flutter http package? - flutter

I tried using http package of flutter and create a custom client with headers.
Code
class ApiClient extends http.BaseClient {
final http.Client _inner;
ApiClient(this._inner);
_setHeaders() => {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer token here...'
};
Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers.addAll(_setHeaders());
return _inner.send(request);
}
}
How can I add a base URL to my custom client?

Since ApiClient inherits http.BaseClient, you should be able to have access to other methods as well. Simply access the method on your ApiClient for example.
var baseUrl = Uri.parse('https://example.com/');
var response = await ApiClient.post(baseUrl);

I use a similar approach on my projects:
class ApiClient extends http.BaseClient {
final http.Client _inner;
final String baseUrl;
ApiClient(this._inner, this.baseUrl);
Uri url(String path, [Map<String, String?>? queryParameters]) {
return Uri.parse('$baseUrl$path').replace(queryParameters: queryParameters);
}
// other methods ...
}
Usage sample:
final api = ApiClient(inner, 'https://testhost/api/v1');
final response = await api.post(api.url('/test', {'q': 'a'}));

Related

How to solve ssl certificate error with GetX and Get Connect in flutter

I am trying to use Getx service.
here is my api client class as am trying to get data from internet using getx
import 'package:flutter_application_shop/utilis/app_constance.dart';
import 'package:get/get.dart';
class ApiClient extends GetConnect implements GetxService {
late String token;
final String appBaseUrl;
late Map<String, String> _mainHeaders;
ApiClient({required this.appBaseUrl}) {
baseUrl = appBaseUrl;
timeout = const Duration(seconds: 30);
token = AppConstance.TOKEN;
_mainHeaders = {
'Content-type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $token',
};
}
Future<Response> getData(String url) async {
try {
Response response = await get(url);
return response;
} catch (e) {
return Response(statusCode: 1, statusText: e.toString());
}
}
///end
}
and when I run debug, I get this error.
I/flutter ( 6967): HandshakeException: Handshake error in client (OS Error:
I/flutter ( 6967): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:393))
How can I solve this?
This is because the request is coming from an untrusted source, in order to bypass the error, Add allowAutoSignedCert = true; to your request in the class that extends GetConnet.
Example:
import 'package:flutter_application_shop/utilis/app_constance.dart';
import 'package:get/get.dart';
class ApiClient extends GetConnect implements GetxService {
late String token;
final String appBaseUrl;
late Map<String, String> _mainHeaders;
ApiClient({required this.appBaseUrl}) {
baseUrl = appBaseUrl;
timeout = const Duration(seconds: 30);
token = AppConstance.TOKEN;
allowAutoSignedCert = true; // the solution
_mainHeaders = {
'Content-type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $token',
};
}
Future<Response> getData(String url) async {
try {
Response response = await get(url);
return response;
} catch (e) {
return Response(statusCode: 1, statusText: e.toString());
}
}
}

How to create a post request in flutter - GetConnect

I am creating a flutter app using GetX, for making server connections I am using GetConnect, I successfully integrated the Get Request but I am unable to integrate Post Request
Here is the piece of code:
const _baseUrl = 'https://support.instagram.com/'; // Dummy api url and key
const Map<String, String> _mapHeaders = {
"ISG-API": "ZMWFDK83NMDF7NM5DF23FI0DBUJ"
};
class ApiService extends GetConnect {
Future<TicketReply> submitTicketReply(String ticketId, String tktreply) async {
String apiUrl = '${_baseUrl}/supportreply';
var body = {
'tktid': ticketId,
'tktreply': tktreply,
};
final response = await post(
apiUrl,
body,
headers: _mapHeaders,
);
print('response: ${response.body}');
if (response.statusCode == 200) {
return TicketReply.fromJson(response.body);
} else {
return Future.error(response.statusText!);
}
}
}
Kindly guide me how to make a successful post request using getconnect
You should add the decoder to the request like so
await post(
apiUrl,
body,
headers: _mapHeaders,
decoder: (resbody) => TicketReply.fromJson(resbody),
);

How can I add customised header on http request for authentication when using flutter graphql library?

I am using this library https://pub.dev/packages/graphql_flutter for graphql in a flutter web application. Below code can be used to get authentication token:
import 'package:graphql_flutter/graphql_flutter.dart';
final HttpLink httpLink = HttpLink(
'https://api.github.com/graphql',
);
final AuthLink authLink = AuthLink(
getToken: () async => 'Bearer <YOUR_PERSONAL_ACCESS_TOKEN>',
// OR
// getToken: () => 'Bearer <YOUR_PERSONAL_ACCESS_TOKEN>',
);
but how can I put the token in the http header like x-api-key: xxxx when sending requests?
I have tried:
HttpLink link = HttpLink(
uri: 'https://api.github.com/graphql',
headers: <String, String>{
'x-api-key': 'xxxx',
},
);
but it gives me the error: The named parameter 'uri' isn't defined. Try correcting the name to an existing named parameter's name, or defining a named parameter with the name 'uri'.
Update: base on the answer from #Moaid
import 'package:graphql_flutter/graphql_flutter.dart';
typedef GetHeaders = FutureOr<Map<String, String>> Function();
class CustomAuthLink extends Link {
CustomAuthLink({
this.getHeaders,
});
final GetHeaders getHeaders;
#override
Stream<Response> request(Request request, [NextLink forward]) {
StreamController<Response> controller;
Future<void> onListen() async {
try {
final Map<String, String> headers = await getHeaders();
return request.updateContextEntry<HttpLinkHeaders>(
(_headers) => HttpLinkHeaders(
headers: <String, String>{
...headers,
},
),
);
} catch (error) {
controller.addError(error);
}
await controller.addStream(forward(request));
await controller.close();
}
controller = StreamController<Response>(onListen: onListen);
return controller.stream;
}
}
Base on the answer from #moaid-alrazhy and after checking how AuthLink is working
class CustomAuthLink extends Link {
CustomAuthLink();
#override
Stream<Response> request(Request request, [NextLink? forward]) async* {
// Some logic here
final AuthService authService = GetIt.I.get<AuthService>();
final String? token = authService.token;
final String deviceID = await DeviceInformation.deviceIMEINumber;
// TIP: do not forget getting new Request instance!
final Request req = request.updateContextEntry<HttpLinkHeaders>(
(HttpLinkHeaders? headers) => HttpLinkHeaders(
headers: <String, String>{
// put oldest headers
...headers?.headers ?? <String, String>{},
// and add a new headers
'Authorization': 'Bearer $token',
'x-device-id': deviceID,
},
),
);
// and "return" new Request with updated headers
yield* forward!(req);
}
}
Probably if you need to change only the name of the Authentication value you can edit the headerKey param
otherwise other parameters can be insert in the "defaultHeaders" fields of the HttpLink object. but I don't know if they can be use for authentication
you can add it to your HttpLink like this
HttpLink link = HttpLink(
'https://api.github.com/graphql',
headers: <String, String>{
'x-api-key': 'xxxx',
},
);
however this was in old versions .. now for more headers your have to write your own CustomAuthLink like
typedef GetHeaders = Future<Map<String, String>> Function();
class CustomAuthLink extends Link {
CustomAuthLink({
this.getHeaders,
}) : super(
request: (Operation operation, [NextLink forward]) {
StreamController<FetchResult> controller;
Future<void> onListen() async {
try {
final Map<String, String> headers = await getHeaders();
operation.setContext(<String, Map<String, String>>{
'headers': headers
});
} catch (error) {
controller.addError(error);
}
await controller.addStream(forward(operation));
await controller.close();
}
controller = StreamController<FetchResult>(onListen: onListen);
return controller.stream;
},
);
GetHeaders getHeaders;
}

Flutter Global Http Interceptor

I would like to know if it is possible to have a global HTTP interceptor to attach token in header for all requests in Flutter? I've searched a lot and couldn't find any information as where and how to set it up as globally. Thanks a lot!
You can extend BaseClient and override send(BaseRequest request):
class CustomClient extends BaseClient {
static Map<String, String> _getHeaders() {
return {
'Authentication': 'c7fabcDefG04075ec6ce0',
};
}
#override
Future<StreamedResponse> send(BaseRequest request) async {
request.headers.addAll(_getHeaders());
return request.send();
}
}
In the above example the 'Authentication': 'c7fabcDefG04075ec6ce0' is hardcoded and not encrypted which you should never do.
Using dio package u can do that :
Dio dio = Dio(BaseOptions(
connectTimeout: 30000,
baseUrl: 'your api',
responseType: ResponseType.json,
contentType: ContentType.json.toString(),
))
..interceptors.addAll(
[
InterceptorsWrapper(onRequest: (RequestOptions requestOptions) {
dio.interceptors.requestLock.lock();
String token = ShareP.sharedPreferences.getString('token');
if (token != null) {
dio.options.headers[HttpHeaders.authorizationHeader] =
'Bearer ' + token;
}
dio.interceptors.requestLock.unlock();
return requestOptions;
}),
// other interceptor
],
);
Flutter provides http_interceptor.dart package.
Sample
class LoggingInterceptor implements InterceptorContract {
#override
Future<RequestData> interceptRequest({RequestData data}) async {
print(data);
return data;
}
#override
Future<ResponseData> interceptResponse({ResponseData data}) async {
print(data);
return data;
}
}
This answer is an extension of Felipe Medeiros's answer that I could not edit. It is not actually a global way to attach a token to every requests, but should be considered nonetheless to create interceptors/middleware.
BaseClient is part of the native http package. You can extend BaseClient and override send(BaseRequest request):
class BearerTokenMiddleware extends BaseClient {
final Future<String> Function() getBearerToken;
BearerTokenMiddleware({required this.getBearerToken});
#override
Future<StreamedResponse> send(BaseRequest request) async {
request.headers.addAll({
'Authorization': 'Bearer ${await getBearerToken()}',
});
return request.send();
}
}
When one of your classes needs the http client, inject the BaseClient abstraction to the constructor. Exemple:
class HTTPTodoGateway implements TodoGateway {
final BaseClient httpClient;
HTTPTodoGateway ({required this.httpClient});
getTodoById(string todoId) {
httpClient.get(Uri.parse('https://mytodos/$todoId'));
}
}
You can then create a new instance of HTTPTodoGateway with an instance of BearerTokenMiddleware that will wrap your requests with an authentication bearer header.

Global configuration (interceptor) for dio in Flutter

First time with Flutter. I'm using dio to send HTTP requests, and I have to add a header to all requests, which I do with an interceptor, like this:
Dio dio = new Dio();
dio.interceptors.add(InterceptorsWrapper(
onRequest:(RequestOptions options) async {
options.headers["X-Requested-With"] = "XMLHttpRequest";
})
);
It works in main.dart, but if I want to import another class like MyHomePage.dart and do HTTP requests there, I'd have to redefine the interceptor in that class too.
How can I implement this interceptor for my whole application without adding it in every .dart file?
Create a function that houses the DIO and then call it where needed
Dio getDio() {
Dio dio = new Dio();
dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
options.headers["X-Requested-With"] = "XMLHttpRequest";
}));
return dio;
}
This worked good for me, without interceptors, just create a class and use it in your app.
import 'package:dio/dio.dart';
import '../helpers/api_url.dart';
class dioClient {
Dio dio = Dio();
static Dio simpleDio() {
return Dio(BaseOptions(
baseUrl: apiUrl(),
headers: {'Content-Type': 'application/json; charset=UTF-8'}));
}
static Dio dioWithCookie(String cookie) {
return Dio(BaseOptions(baseUrl: apiUrl(), headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Cookie': cookie
}));
}
}