第一次提交
This commit is contained in:
146
lib/providers/api.dart
Normal file
146
lib/providers/api.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../config.dart';
|
||||
import '../models/class_cookie_entry.dart';
|
||||
|
||||
const _cookieKey = AppSharedPreferencesKey.cookieKey;
|
||||
|
||||
final cookieProvider = StateNotifierProvider<CookieNotifier, Map<String, CookieEntry>>((ref) {
|
||||
return CookieNotifier();
|
||||
});
|
||||
|
||||
final cookieHeaderProvider = Provider<String>((ref) {
|
||||
final cookies = ref.watch(cookieProvider);
|
||||
final now = DateTime.now();
|
||||
|
||||
return cookies.entries
|
||||
.where((e) => e.value.expires == null || e.value.expires!.isAfter(now))
|
||||
.map((e) => e.value.toCookieString(e.key))
|
||||
.join('; ');
|
||||
});
|
||||
|
||||
|
||||
final apiClientProvider = Provider<ApiClient>((ref) {
|
||||
final cookieHeader = ref.watch(cookieHeaderProvider);
|
||||
final client = ApiClient(ref: ref);
|
||||
ref.onDispose(client.dispose);
|
||||
return client;
|
||||
});
|
||||
|
||||
class ApiClient {
|
||||
final Ref ref;
|
||||
final http.Client _client;
|
||||
|
||||
static const String baseUrl = AppConfig.apiBaseUrl;
|
||||
|
||||
ApiClient({required this.ref, http.Client? client})
|
||||
: _client = client ?? http.Client(); // 复用或新建
|
||||
|
||||
Map<String, String> _buildHeaders({Map<String, String>? extra}) {
|
||||
final cookieHeader = ref.read(cookieHeaderProvider);
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
if (cookieHeader.isNotEmpty) 'Cookie': cookieHeader,
|
||||
...?extra,
|
||||
};
|
||||
}
|
||||
|
||||
Future<http.Response> _send(Future<http.Response> Function(http.Client client) request) async {
|
||||
final response = await request(_client);
|
||||
|
||||
final setCookie = response.headers['set-cookie'];
|
||||
if (setCookie != null) {
|
||||
await ref.read(cookieProvider.notifier).updateFromSetCookie(setCookie.split(','));
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<http.Response> get(String path, {Map<String, String>? headers}) {
|
||||
return _send((client) => client.get(
|
||||
Uri.parse('$baseUrl/$path'),
|
||||
headers: _buildHeaders(extra: headers),
|
||||
));
|
||||
}
|
||||
|
||||
Future<http.Response> post(String path, {Map<String, String>? headers, String? body}) {
|
||||
return _send((client) => client.post(
|
||||
Uri.parse('$baseUrl/$path'),
|
||||
headers: _buildHeaders(extra: headers),
|
||||
body: body,
|
||||
));
|
||||
}
|
||||
|
||||
void _checkResponse(http.Response res) {
|
||||
if (res.statusCode >= 300) {
|
||||
throw Exception('HTTP ${res.statusCode}: ${res.body}');
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_client.close(); // 记得释放资源
|
||||
}
|
||||
}
|
||||
|
||||
class CookieNotifier extends StateNotifier<Map<String, CookieEntry>> {
|
||||
CookieNotifier() : super({}) {
|
||||
_load();
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final list = prefs.getStringList(_cookieKey) ?? [];
|
||||
final map = <String, CookieEntry>{};
|
||||
|
||||
for (final item in list) {
|
||||
final split = item.split('|');
|
||||
if (split.length == 2) {
|
||||
final key = split[0];
|
||||
final valueMap = Map<String, dynamic>.from(jsonDecode(split[1]));
|
||||
map[key] = CookieEntry.fromJson(valueMap);
|
||||
}
|
||||
}
|
||||
|
||||
state = map;
|
||||
}
|
||||
|
||||
Future<void> _persist() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final list = state.entries.map((e) => '${e.key}|${jsonEncode(e.value.toJson())}').toList();
|
||||
await prefs.setStringList(_cookieKey, list);
|
||||
}
|
||||
|
||||
Future<void> setAll(Map<String, CookieEntry> map) async {
|
||||
state = Map<String, CookieEntry>.from(map);
|
||||
await _persist();
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
state = {};
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_cookieKey);
|
||||
}
|
||||
|
||||
Future<void> updateFromSetCookie(List<String> setCookies) async {
|
||||
final updated = Map<String, CookieEntry>.from(state);
|
||||
|
||||
for (final raw in setCookies) {
|
||||
final parts = raw.split(';');
|
||||
final kv = parts[0].split('=');
|
||||
if (kv.length < 2) continue;
|
||||
final name = kv[0].trim();
|
||||
final entry = CookieEntry.fromSetCookie(raw);
|
||||
updated[name] = entry;
|
||||
}
|
||||
|
||||
state = updated;
|
||||
await _persist();
|
||||
}
|
||||
|
||||
bool get isLoggedIn =>
|
||||
state.containsKey('auth') || state.containsKey('sessionid');
|
||||
}
|
Reference in New Issue
Block a user