authenticated_http_client 0.2.5
authenticated_http_client: ^0.2.5 copied to clipboard
An advanced authenticated HTTP client introduces `factory` feature that generates request futures from API definitions in WYSIWYG style, with extra support for `mock`, `silent`.
authenticated_http_client #
一个支持登陆态管理的 HTTP 客户端,提供 factory 生产方法,可按 API 声明以所见即所得(WYSIWYG)的方式生成请求 Future,同时在开发阶段额外支持 mock 与 silent 模式。
一切从如下简洁的范式约定开始 —— [mock] [silent] [method] /api/plan/{id}/details —— 让HTTP请求变得简单可见。
factory:按 API URI 声明以所见即所得方式生成 AJAX 请求函数。mock:在开发阶段,为指定请求从本地 JSON 文件返回模拟数据。silent:为指定请求在遇到未授权或维护响应时抑制路由跳转。throttling:当开启限流时,超量请求将排队(基于 async/await)以控制上传节奏。- 生成的请求 Future 函数可以通过链式访问。
语言: 中文 | English
平台支持 #
| Android | iOS | MacOS | Web | Linux | Windows |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ❌️ | ✅ | ✅ |
依赖要求 #
- Flutter >=3.0.0 <4.0.0
- Dart: ^2.17.0
- http: ^1.3.0
- http_interceptor: ^2.0.0
开始使用 #
该库已发布在 pub.flutter-io.cn,运行以下 Flutter 命令安装:
flutter pub add authenticated_http_client
用法演示 #
- 获取
auth_token并将其保存到AuthenticatedHttpClient的缓存中。
var apiService = AuthenticatedHttpClient.getInstance().factory({ "login" : "POST /api/sign-in" });
apiService.login({"username": "demo", "passwords": "test123"}).then((response) {
// 响应体格式:{code, message, data}
final {"auth_token": authToken, "expired_at": expiredAt} = response["data"];
AuthenticatedHttpClient.getInstance().setAuthToken(authToken);
});
- 创建支持路径参数的请求 Future。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"requestName" : "POST /api/submit/plan",
"requestNameWithColonParams" : "GET /api/plan/:id/details",
"requestNameWithBraceParams" : "GET /api/plan/{id}/details"
});
apiService.requestName().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
apiService.requestNameWithColonParams({"id": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
apiService.requestNameWithBraceParams({"id": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
mock请求将从mockDirectory(默认/lib/mock)中的 JSON 文件加载。可通过AuthenticatedHttpClient.getInstance().init()配置mockDirectory,并需在pubspec.yaml的 assets 段落中声明。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"mockRequest" : "MOCK POST /api/task/config",
});
// 将从 mockDirectory /lib/mock 下的 _post_api_task_config.json 读取模拟数据
apiService.mockRequest().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
silent静默请求在遇到未授权响应或维护中时不会触发路由跳转。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"silentRequest" : "SILENT GET /api/message/check/unread"
});
// 即便返回 401 也不会跳转到登录页
apiService.silentRequest().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
- 限流控制:当请求过多时进行排队。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"request" : "SILENT POST /api/upload"
});
apiService.request(throttling: true).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
- 每个请求都会被标记一个唯一 ID(如
requestId)用于追踪,也可通过命名参数传入。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"request" : "SILENT POST /api/upload"
});
String requestId = Uuid().v1();
print("gonna send request with unique id : $requestId");
apiService.request(requestId: requestId).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
AuthenticatedHttpClient.all为一个静态函数,类似 Promise.all,并带有可选延迟功能以避免服务器并发导致的潜在问题。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"requestName" : "POST /api/submit/plan",
"requestNameWithParams" : "GET /api/plan/:id/details",
});
List<Future> futures = [apiService.requestName(), apiService.requestNameWithParams({"id": 9528})];
AuthenticatedHttpClient.all(futures, delayInMilliSecs: 350).then((results){
print(results['0']); // 第 1 个请求的响应
print(results['1']); // 第 2 个请求的响应
});
在 Dart 中的使用步骤 #
- 初始化一个
RouterHelper,用于在必要时控制何时以及如何跳转至登录页或维护页。
import 'package:authenticated_http_client/router_helper.dart';
RouterHelper.init(
jump2LoginCallback: () { /* 依据你的路由库跳转到登录页 */ },
unAuthCode: "101|103"
);
使用不同路由库实现 `jump2LoginCallback` 的示例
// FluroRouter 示例
BuildContext context; // 保存当前构建上下文
function jump2LoginCallback() {
String? currentRoute = ModalRoute.of(context)?.settings.name;
if(currentRoute?.split("?").first == "/login") { return ; }
FluroRouter().navigateTo(context, "/login", clearStack: true);
}
// GoRouter 示例
BuildContext context; // 保存当前构建上下文
function jump2LoginCallback() {
String? currentRoute = GoRouter.of(context).location;
if(currentRoute?.split("?").first == "/login") { return ; }
GoRouter(routes: []).go("/login");
}
- 实现
CustomHttpHeadersInterceptor,稍后初始化AuthenticatedHttpClient时会用到。
import 'package:authenticated_http_client/http_headers_interceptor.dart';
class Constants {
static String udid = "357292741221214";
static String appVersion = "1.0.1";
static String appBuildNumber = "1394";
static String systemCode = "e08f11c8-3b1c-4008-abba-045787e0b6c0";
}
class CustomHttpHeadersInterceptor extends HttpHeadersInterceptor {
@override
Map<String, String> headersInterceptor(Map<String, String> headers) {
headers["udid"] = Constants.udid;
headers["version"] = Constants.appVersion;
headers["system-code"] = Constants.systemCode;
return headers;
}
}
- 初始化已认证 HTTP 客户端,之后每个 AJAX 请求都会在 Authorization 头中附带 token。
import 'package:authenticated_http_client/authenticated_http_client.dart';
AuthenticatedHttpClient.getInstance().init(
"https://api.company.com",
customHttpHeadersInterceptor: CustomHttpHeadersInterceptor()
);
- 最终,你可以按需使用任意功能。
var apiService = AuthenticatedHttpClient.getInstance().factory({
"login" : "POST /api/sign-in",
"requestName" : "POST /api/submit/plan",
"requestNameWithParams" : "GET /api/plan/:id/details", // 或 "GET /api/plan/{id}/details"
"mockRequest" : "MOCK POST /api/task/config", // 将从 mockDirectory /lib/mock 下的 _post_api_task_config.json 读取模拟数据
"silentRequest" : "SILENT GET /api/message/check/unread" // 静默请求在未授权或维护时不跳转
});
apiService.login({"username": "demo", "passwords": "test123"}).then((response) {
// 响应体格式:{code, message, data}
final {"auth_token": authToken, "expired_at": expiredAt} = response["data"];
AuthenticatedHttpClient.getInstance().setAuthToken(authToken);
});
apiService.requestName().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
apiService.requestNameWithParams({"id": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });
/// AuthenticatedHttpClient.all 是一个静态函数,类似 Promise.all,
/// 并引入了延迟功能以避免潜在的服务器并发问题
List<Future> futures = [apiService.requestName(), apiService.requestNameWithParams({"id": 9528})];
AuthenticatedHttpClient.all(futures, delayInMilliSecs: 350).then((results){
print(results['0']); // 第 1 个请求的响应
print(results['1']); // 第 2 个请求的响应
});
其他信息 #
如有问题,欢迎提交 Issue。