Create Igel
This commit is contained in:
+38
-19
@@ -1,10 +1,17 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
/// Lightweight API-Client mit einfachem 401-Retry via Refresh-Token.
|
||||
class ApiClient {
|
||||
ApiClient(this.baseUrl, {this.getAccessToken});
|
||||
final String baseUrl; // z.B. https://api.example.com
|
||||
ApiClient(
|
||||
this.baseUrl, {
|
||||
this.getAccessToken,
|
||||
this.onUnauthorized, // z.B. () => tokenStorage.refreshAccess()
|
||||
});
|
||||
|
||||
final String baseUrl; // z.B. https://api.windesign.at/hedgehogs.php?r=
|
||||
final Future<String?> Function()? getAccessToken;
|
||||
final Future<void> Function()? onUnauthorized;
|
||||
|
||||
Future<http.Response> _send(String method, String path,
|
||||
{Object? body}) async {
|
||||
@@ -12,13 +19,16 @@ class ApiClient {
|
||||
final headers = {'Content-Type': 'application/json'};
|
||||
final token = await getAccessToken?.call();
|
||||
if (token != null) headers['Authorization'] = 'Bearer $token';
|
||||
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return http.get(uri, headers: headers);
|
||||
case 'POST':
|
||||
return http.post(uri, headers: headers, body: jsonEncode(body));
|
||||
return http.post(uri,
|
||||
headers: headers, body: body == null ? null : jsonEncode(body));
|
||||
case 'PUT':
|
||||
return http.put(uri, headers: headers, body: jsonEncode(body));
|
||||
return http.put(uri,
|
||||
headers: headers, body: body == null ? null : jsonEncode(body));
|
||||
case 'DELETE':
|
||||
return http.delete(uri, headers: headers);
|
||||
default:
|
||||
@@ -26,23 +36,32 @@ class ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> get(String path) async =>
|
||||
_decode(await _send('GET', path));
|
||||
Future<Map<String, dynamic>> post(
|
||||
String path, Map<String, dynamic> body) async =>
|
||||
_decode(await _send('POST', path, body: body));
|
||||
Future<Map<String, dynamic>> put(
|
||||
String path, Map<String, dynamic> body) async =>
|
||||
_decode(await _send('PUT', path, body: body));
|
||||
Future<Map<String, dynamic>> delete(String path) async =>
|
||||
_decode(await _send('DELETE', path));
|
||||
Map<String, dynamic> _decode(http.Response r) {
|
||||
if (r.statusCode >= 200 && r.statusCode < 300) {
|
||||
if (r.body.isEmpty) return {};
|
||||
return jsonDecode(r.body) as Map<String, dynamic>;
|
||||
Future<T> _requestWithRetry<T>(String method, String path,
|
||||
{Object? body}) async {
|
||||
http.Response res = await _send(method, path, body: body);
|
||||
|
||||
// Bei 401 einmal Refresh versuchen
|
||||
if (res.statusCode == 401 && onUnauthorized != null) {
|
||||
await onUnauthorized!.call();
|
||||
res = await _send(method, path, body: body);
|
||||
}
|
||||
throw ApiException(r.statusCode, r.body);
|
||||
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
if (res.body.isEmpty) return (null as T);
|
||||
final decoded = jsonDecode(res.body);
|
||||
return decoded as T; // Aufrufer achtet auf Typ (Map/List)
|
||||
}
|
||||
|
||||
throw ApiException(res.statusCode, res.body);
|
||||
}
|
||||
|
||||
Future<T> get<T>(String path) async => _requestWithRetry<T>('GET', path);
|
||||
Future<T> post<T>(String path, Object body) async =>
|
||||
_requestWithRetry<T>('POST', path, body: body);
|
||||
Future<T> put<T>(String path, Object body) async =>
|
||||
_requestWithRetry<T>('PUT', path, body: body);
|
||||
Future<T> delete<T>(String path) async =>
|
||||
_requestWithRetry<T>('DELETE', path);
|
||||
}
|
||||
|
||||
class ApiException implements Exception {
|
||||
|
||||
Reference in New Issue
Block a user