Flutter API调用代理插件api_agent的使用
Flutter API调用代理插件api_agent的使用
api_agent
是一个技术无关的API绑定库,适用于您的全栈Dart应用。
主要特性
api_agent
通过从单一源定义生成类型安全的客户端和服务端绑定,来桥接Dart前端和后端之间的服务缺口。它会自动保持客户端和服务端的API定义同步。- 这个包只关注API的结构,而不是使用的具体技术和协议。因此它是完全技术无关且协议无关的,并且您对数据传输层有完全控制权。它提供了针对常见协议(如HTTP)的现成实现,或者您可以根据需要实现自己的协议支持(例如 Websockets, MQTT, GRPC, GraphQL 或任何专有协议)。
目录
概述
在共享位置定义您的API就像定义一个抽象类一样简单。
@ApiDefinition()
abstract class MyApi {
Future<MyResult> myApiEndpoint();
}
在客户端访问API就像调用一个方法一样简单。
void main() async {
var client = MyApiClient( // 从您的API定义生成
HttpApiClient( // 现成的HTTP客户端
domain: "http://my.domain.com", // HTTP客户端特定选项
),
);
var result = await client.myApiEndpoint(); // 从您的API定义生成的方法
}
在服务端实现API就像实现一个抽象方法一样简单。
void main() async {
var server = await serve( // 从包:shelf
ShelfApiRouter([ // 现成的shelf路由器
MyApiEndpointImpl(),
]),
InternetAddress.anyIPv4, port
);
}
class MyApiEndpointImpl extends MyApiEndpoint { // 从您的API定义生成
@override
Future<MyResult> myApiEndpoint(ApiRequest request) {
// 您的实现在这里
}
}
设置与入门
对于使用api_agent
的全栈Dart应用,您需要以下设置:
首先创建一个新的工作空间文件夹:
mkdir my_app && cd my_app
首先创建一个shared
Dart项目,这将包含前后端都会使用的所有代码。在这种情况下,我们将API定义放在这个项目中。
dart create my_app_shared
cd my_app_shared
这将创建一个简单的Dart应用项目。由于我们希望将其作为共享库使用,所以我们不需要生成的bin
目录,而是需要一个包含definitions.api.dart
的lib
目录。
rm -r bin
mkdir lib && touch lib/definitions.api.dart
我们还需要将api_agent
作为依赖项添加,同时将build_runner
作为开发依赖项添加。
dart pub add api_agent
dart pub add build_runner --dev
接下来我们在创建的文件中添加我们的API定义:
import 'package:api_agent/api_agent.dart';
@ApiDefinition()
abstract class GreetApi {
Future<String> greet(String name);
}
最后,在共享项目中运行build_runner
以生成前后端所需的API绑定。这将生成definitions.client.dart
和definitions.server.dart
,分别用于前后端。
dart run build_runner build
前端项目设置
接下来我们创建前端项目。这可以是一个命令行应用程序,也可以是一个使用Dart编写的Flutter应用或网站。为了简化起见,我们将创建一个简单的命令行应用,但所有客户端应用的用法都是相同的。
cd .. # 确保我们在工作区目录中
dart create my_app_frontend
我们同样需要将api_agent
作为依赖项添加。此外,我们还需要将my_app_shared
项目作为本地路径依赖项添加。
cd my_app_frontend
dart pub add api_agent
dart pub add my_app_shared --path=../my_app_shared
现在打开生成的bin/my_app_frontend.dart
并更改其内容如下。这将导入共享包中的生成API绑定,并使用它设置基本的HTTP客户端。
import 'package:api_agent/clients/http_client.dart';
import 'package:my_app_shared/definitions.client.dart';
void main(List<String> args) async {
var client = GreetApiClient(
HttpApiClient(domain: "http://localhost:8080"),
);
var result = await client.greet(args[0]);
print(result);
}
后端项目设置
最后我们设置后端项目。这可以使用任何结构或包,但为了简化起见,我们将使用标准的package:shelf
应用。
cd .. # 确保我们在工作区目录中
dart create -t server-shelf my_app_backend
像往常一样,我们需要在api_agent
和我们的my_app_shared
项目上添加所需的依赖项。
cd my_app_backend
dart pub add api_agent
dart pub add my_app_shared --path=../my_app_shared
接下来我们修改生成的bin/server.dart
。我们可以保留大部分设置,只需插入自定义路由器及其API实现。因此将第7到19行替换为以下内容:
import 'package:api_agent/servers/shelf_router.dart';
import 'package:my_app_shared/definitions.server.dart';
final _router = ShelfApiRouter([GreetApiImpl()]);
class GreetApiImpl extends GreetApiEndpoint {
@override
String greet(String name, ApiRequest _) {
return 'Hello $name.';
}
}
使用
正如在“设置与入门”部分所展示的那样,使用api_agent
的全栈应用有三个主要部分:
- 共享项目中的API定义
- 前端项目的API客户端
- 后端项目的API端点
API定义
通过使用@ApiDefinition()
注解在抽象类上定义API定义。该类中的抽象方法定义了API支持的端点。一个方法可以具有任意数量的必需或可选参数,并且必须返回任何类型的Future
。
@ApiDefinition()
abstract class GreetApi {
Future<String> greet(String name);
Future<MyResult> compute(MyData data, {bool? someOptionalParam});
}
一个ApiDefinition
可以通过定义具有任何名称的抽象getter
来包含任意数量的嵌套子ApiDefinition
。返回类型必须是另一个ApiDefinition
。
@ApiDefinition()
abstract class MainApi {
UserApi get users;
}
@ApiDefinition()
abstract class UserApi {
Future<List<User>> list();
}
如何处理嵌套的ApiDefinition
取决于协议实现。例如,HTTP实现会根据嵌套的API定义和目标方法构造请求URL。在上述情况下,调用MainApiClient().users.list()
将导致URL路径为/users/list
。
API客户端
api_agent
为每个API定义生成API客户端。要使用客户端,实例化它时传入一个协议客户端作为唯一参数:
var client = GreetApiClient( // 从您的API定义生成
HttpApiClient(), // 特定协议客户端
);
var result = await client.greet();
api_agent
已经包含了以下客户端:
HttpApiClient
这将使用http
包向您的API发出请求。
- 当方法前缀为
get
时(例如getUsers
),将使用GET
,否则使用POST
- 对于
GET
请求,参数作为查询参数编码 - 对于
POST
请求,参数作为JSON有效载荷发送 - URL由类名和方法名按以下方式构造:
- 类名中的
Api
后缀在URL中被忽略(GreetApi
->/greet
) - 方法名中的
get
前缀在URL中被忽略(getUsers()
->/users
) - 方法名和类名被转换为蛇形命名(
myMethodName()
->/my_method_name
)
- 类名中的
HttpApiClient
构造函数接受:
domain
,您的API托管的位置- 可选的
path
,您的API挂载的位置,默认为/
- 可选的
ApiProvider
列表 - 可选的
ApiCodec
作为默认值,如果API定义中未定义
ApiProviders
ApiProviders
可以在请求发送之前对其进行拦截和修改。一个常见的用例是向请求添加认证头:
class AuthProvider extends ApiProvider<HttpApiRequest> {
@override
FutureOr<HttpApiRequest> apply(HttpApiRequest request) {
return request.change(headers: {'Authorization': 'Bearer my-bearer-token'});
}
}
自定义API客户端编写
要定义一个使用您选择的协议的自定义客户端,只需实现ApiClient
接口:
class MyProtocolApiClient implements ApiClient {
@override
ApiClient mount(String prefix, [ApiCodec? codec]) {
}
@override
Future<T> request<T>(String endpoint, Map<String, dynamic> params) {
}
}
mount()
方法应该返回一个新的API客户端实例,尊重给定的前缀和编解码器。
request()
方法应该通过所选协议和技术实际执行API调用。它接收请求的端点名称(调用客户端方法的名称)和提供的参数映射。一个标准实现可能包括:
- 构造一个
ApiRequest
实例,可能是特定协议的子类,如HttpApiRequest
- 通过
request.apply(providers)
应用任何给定的ApiProviders
- 执行请求(特定于协议的实现)
- 返回结果,可能使用
codec.decode<T>(result)
进行解码
通常最好查看现有API客户端的实现,如包含的HttpApiClient
。
API端点
api_agent
为每个ApiDefinition
生成ApiEndpoint
。ApiEndpoint
需要在服务器端实现,并传递给一个ApiBuilder
,后者例如启动一个HTTP服务器并将传入的请求传递给您的端点。
要实现您的端点,您需要做以下操作:
首先,我们上面的API定义将生成一个GreetEndpoint
,它期望一个形式的实现FutureOr<String> Function(String name, ApiRequest request)
。
有许多方法可以实现一个端点。
- 传递处理程序函数
var greetEndpoint = GreetEndpoint.from((name, r) {
return 'Hello $name.';
});
- 扩展抽象类
var greetEndpoint = GreetEndpointImpl();
class GreetEndpointImpl extends GreetEndpoint {
@override
FutureOr<String> greet(String name, ApiRequest request) {
return 'Hello $name.';
}
}
您可以根据具体情况自由选择这两种选项之一。
接下来,api_agent
还将生成一个GreetApiEndpoint
,它期望一个GreetEndpoint
作为其子节点。您可以再次选择直接提供端点,或者实现抽象类:
- 传递子端点
var greetApi = GreetApiEndpoint.from(
greet: greetEndpoint,
);
- 扩展抽象类
var greetApi = GreetApiEndpointImpl();
class GreetApiEndpointImpl extends GreetApiEndpoint {
@override
FutureOr<String> greet(String name, ApiRequest request) {
return 'Hello $name.';
}
}
这可能看起来有些混乱,但它给您提供了选择合适的一致性级别的自由。对于小型简单的API,您可能不希望为每个端点创建一个自定义类。但对于较大且复杂的API,您可能会发现将逻辑分离出来更合适。
API中间件
在具有嵌套内部API的更复杂API中,您的端点将形成树状结构:
var myApi = MyApiEndpoint.from(
users: UsersEndpoint.from(
list: ...,
getById: ...,
),
publications: PublicationsEndpoint.from(
articles: ArticlesEndpoint.from(
list: ...,
...
),
),
)
您可以在树中的任何位置添加ApiMiddleware
,以监控或保护所选子树中的端点请求。一个典型的用例是验证请求用户。
通过实现ApiMiddleware
接口来实现自定义的ApiMiddleware
:
class AuthMiddleware implements ApiMiddleware {
@override
FutureOr<dynamic> apply(covariant ShelfApiRequest request, EndpointHandler next) {
String? token = request.headers['Authorization'];
if (validateToken(token)) {
return next(request);
} else {
throw ApiException(401, 'Authentication token is invalid.');
}
}
}
然后,使用ApplyMiddleware
端点插入您的中间件:
var myApi = MyApiEndpoint.from(
users: ApplyMiddleware(
middleware: AuthMiddleware(),
child: UserEndpoint.from( // 中间件应用于所有子端点
...
),
),
publications: PublicationsEndpoint.from( // 中间件不应用于端点
...
),
);
API构建器
ApiBuilder
消耗您的API端点并构建客户端可以连接的通信通道。api_agent
已经包含了以下构建器:
ShelfApiRouter
这将使用shelf_router
包构建一个路由器,该路由器可以与shelf
包一起使用,并例如传递给serve
。
它旨在与HttpApiClient
一起使用,并具有相同的要求规则。
要使用它,只需将您的API端点传递给ShelfApiRouter
构造函数并将其用作shelf处理器:
void main() {
var router = ShelfApiRouter([greetApi]);
serve(router, InternetAddress.anyIPv4, port: 8080);
}
自定义API构建器编写
要定义一个使用您选择的协议的自定义API构建器,只需实现ApiBuilder
接口:
class MyProtocolApiBuilder implements ApiBuilder {
@override
void mount(String prefix, List<ApiEndpoint> children) {
// 在[prefix]下安装[children]
// 应调用[ApiEndpoint.build()]为每个子项创建新的API构建器实例
}
@override
void handle(String endpoint, EndpointHandler handler) {
// 注册此[endpoint]的[handler]
}
}
之后,您应该调用您的根ApiEndpoint
(s)的build(ApiBuilder builder)
方法,并提供您的自定义ApiBuilder
实例。这些将随后调用mount()
或handle()
以与您的API注册。
建议查看现有API构建器的实现,如包含的ShelfApiRouter
。
示例代码
下面是一个完整的示例代码,展示了如何使用api_agent
在Flutter应用中调用API。
// 共享API定义
import 'package:api_agent/api_agent.dart';
@ApiDefinition()
abstract class GreetApi {
Future<String> greet(String name);
}
// 客户端代码
import 'package:api_agent/clients/http_client.dart';
Future<void> clientMain() async {
var client = GreetApiClient(
HttpApiClient(domain: 'http://localhost:8080'),
);
var result = await client.greet('James');
print(result); // 打印 'Hello James.'
}
// 服务端代码
import 'package:api_agent/servers/shelf_router.dart';
import 'package:shelf/shelf_io.dart';
void serverMain() async {
var api = ShelfApiRouter([
GreetApiEndpoint.from(
greet: GreetEndpoint.from((name, _) {
return 'Hello $name.';
}),
),
]);
serve(api, InternetAddress.anyIPv4, 8080);
}
测试
要测试您的应用,可以在两个单独的终端窗口中启动服务器和客户端:
# 终端1: 运行服务器
cd my_app_backend && dart run bin/server.dart
# 终端2: 运行客户端
cd my_app_frontend && dart run bin/my_app_frontend.dart James
更多关于Flutter API调用代理插件api_agent的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter API调用代理插件api_agent的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用代理插件 api_agent
进行API调用的代码示例。这个示例假定你已经安装并配置好了 api_agent
插件。
首先,确保你已经在 pubspec.yaml
文件中添加了 api_agent
依赖:
dependencies:
flutter:
sdk: flutter
api_agent: ^最新版本号 # 替换为实际的最新版本号
然后运行 flutter pub get
来获取依赖。
接下来,你可以在你的 Flutter 项目中配置和使用 api_agent
。以下是一个简单的示例,展示了如何使用该插件进行API调用:
1. 配置代理设置
首先,你需要配置代理设置。这通常在应用的初始化阶段完成。
import 'package:flutter/material.dart';
import 'package:api_agent/api_agent.dart';
void main() {
// 初始化 ApiAgent 配置
ApiAgentConfig config = ApiAgentConfig(
baseUrl: 'https://api.example.com', // 替换为你的API基础URL
timeout: Duration(seconds: 30), // 设置请求超时时间
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN', // 替换为你的实际访问令牌
},
// 代理设置(如果需要的话)
proxy: {
'http': 'http://your-proxy-server:port',
'https': 'http://your-proxy-server:port',
'noProxy': 'localhost,127.0.0.1' // 不通过代理的主机或IP
},
);
// 设置全局配置
ApiAgent.instance.config = config;
runApp(MyApp());
}
2. 创建一个API服务类
创建一个服务类来封装你的API调用逻辑。
import 'package:api_agent/api_agent.dart';
class ApiService {
// 获取用户信息的API调用
Future<Map<String, dynamic>> getUserInfo(String userId) async {
Map<String, dynamic> params = {
'user_id': userId,
};
try {
ApiResponse<Map<String, dynamic>> response = await ApiAgent.instance.get(
'/user/info',
params: params,
);
if (response.isSuccessful) {
return response.data;
} else {
throw Exception('API调用失败: ${response.statusCode} - ${response.message}');
}
} catch (e) {
throw Exception('获取用户信息失败: $e');
}
}
}
3. 在UI中使用API服务
在你的Flutter UI中使用上述服务类来调用API并显示数据。
import 'package:flutter/material.dart';
import 'api_service.dart'; // 导入你创建的API服务类
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _userInfo = '';
ApiService _apiService = ApiService();
@override
void initState() {
super.initState();
_fetchUserInfo('12345'); // 替换为实际的用户ID
}
Future<void> _fetchUserInfo(String userId) async {
try {
Map<String, dynamic> user = await _apiService.getUserInfo(userId);
setState(() {
_userInfo = jsonEncode(user); // 假设你想以JSON格式显示用户信息
});
} catch (e) {
setState(() {
_userInfo = 'Error: $e';
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter API调用示例'),
),
body: Center(
child: Text(_userInfo),
),
),
);
}
}
这个示例展示了如何使用 api_agent
插件在Flutter应用中配置代理、封装API调用逻辑,并在UI中显示API调用的结果。请根据你的实际需求调整URL、请求参数和错误处理等细节。