Flutter网络请求插件http_client_plugin的使用

Flutter网络请求插件http_client_plugin的使用

Http Client Plugin

Http Client Plugin 是一个由 Tachyon 驱动的代码生成器。

该插件允许你使用 Dio 作为 HTTP 客户端来生成 HTTP 服务。

如何工作

Http Client Plugin 使用 Tachyon 作为其构建引擎以提供快速的代码生成。此外,该插件使用 analyzer 系统和 analyzer plugin 来访问源代码,解析它并基于此提供 code actions

这些 code actions 类似于语言提供的功能(例如 wrap with try/catch),因此你无需依赖代码片段或手动输入任何样板代码。

安装

  1. 在你的项目的 pubspec.yaml 文件中添加:
dependencies:
  http_client_plugin: any

dev_dependencies:
  tachyon: any
  1. 在项目的根目录下创建 tachyon_config.yaml 文件:
file_generation_paths: # 哪些文件/路径包含在构建中
  - "file/path/to/watch"
  - "another/one"

generated_file_line_length: 80 # 默认行长度

plugins:
  - http_client_plugin # 注册 http_client_plugin
  1. 更新你的 analysis_options.yaml 文件(以启用 code actions):

最小化 analysis_options.yaml

include: package:lints/recommended.yaml

# 你需要在 analyzer > plugins 下注册插件
analyzer:
  plugins:
    - http_client_plugin
  1. 重启分析服务器

VSCode

  1. 打开命令面板
    • Windows/Linux: Ctrl + Shift + P
    • MacOS: ⌘ + Shift + P
  2. 输入并选择 “Dart: Restart Analysis Server”

IntelliJ

  1. 打开查找操作
    • Windows/Linux: Ctrl + Shift + A
    • MacOS: ⌘ + Shift + A
  2. 输入并选择 “Restart Dart Analysis Server”

生成你想要的代码

HttpService 注解
  1. 创建一个简单的类,并用 [@HttpService](/user/HttpService)() 进行注解:
[@HttpService](/user/HttpService)()
class AppHttpService { ... }
  1. 添加一个抽象方法,该方法用 [@HttpMethod](/user/HttpMethod).* 注解,并且返回类型为 FutureHttpResult<*>
[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/user')
   FutureHttpResult<User> getUser([@QueryParam](/user/QueryParam)() int userId);

}
  1. 在 IDE 中运行代码动作

VSCode

  • Windows/Linux: Ctrl + .
  • MacOS: ⌘ + .

IntelliJ

  • Windows/Linux: Alt + Enter
  • MacOS: ⌘ + Enter
  1. 选择 Generate http services

这将生成所有源文件中的样板代码。

现在为了生成部分文件代码,你需要运行 tachyon

执行 tachyon:

dart run tachyon build

更多关于 tachyon 的选项可以通过执行 dart run tachyon --help 查看。

注解

HttpService

注解一个类为 HTTP 服务类。

接收一个可选的 path 参数。

示例:

[@HttpService](/user/HttpService)('/api/v0') // 所有路径将以 /api/v0 开头
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/ping') // 这将变成基础 URL + /api/v0 + /ping
   FutureHttpResult<void> ping();

}
HttpMethod

注解一个方法以指定使用的 HTTP 动词。可用的 HttpMethod 动词包括:

  • HttpMethod.get()
  • HttpMethod.post()
  • HttpMethod.put()
  • HttpMethod.delete()

接收一个必需的 path 参数。路径可以为空字符串。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/users')
   FutureHttpResult<List<User>> getUsers();

}
QueryParam

注解一个方法的参数为查询参数。

接收一个可选的替代名称作为查询参数。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/user')
   FutureHttpResult<User> getUser(
      // 查询参数将设置为 "userId"。参见下一个示例如何更改
      [@QueryParam](/user/QueryParam)() int userId
   );

}

提供不同的查询参数名称:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/user')
   FutureHttpResult<User> getUser([@QueryParam](/user/QueryParam)('user_id') int userId);

}
PathParam

注解一个方法的参数为路径参数。

HttpMethod 注解一起使用时,如果路径段以 : 开始,且匹配段名的注解参数([@PathParam](/user/PathParam)),则参数值将替换该段。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).get('/user/:id') // 这将变成 /user/<id 的值>
   FutureHttpResult<User> getUser([@PathParam](/user/PathParam)() int id);

}
HttpPayload

注解一个方法的参数为请求的有效载荷。

这可以是任何基本类型(或包含基本数据的列表或映射)。还内置支持 Uri(使用 toString)和 DateTime(使用 toIso8601String)。

还可以使用包含 toJson 方法返回上述内容的类对象。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).post('/user/:id')
   FutureHttpResult<User> updateUser(@HttpPayload() Map<String, dynamic> data);

}
HttpHeader

注解一个方法以添加额外的 HTTP 头。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).post('/user/:id')
   @HttpHeader('Content-Type', 'application/json')
   @HttpHeader('Accept', 'application/json')
   FutureHttpResult<User> updateUser(@HttpPayload() Map<String, dynamic> data);

}
HttpMultipart

注解一个方法为多部分请求。

可选地接受一个参数 listFormat,指示列表应如何格式化,默认值为 MultipartListFormat.multi。更多选项可以在 这里 查看。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).post('/user')
   [@HttpMultipart](/user/HttpMultipart)()
   FutureHttpResult<User> updateUser({
      @HttpFormField() required String username,
      @HttpFormField() required String password,
   });

}
HttpFormField

注解一个方法的参数为多部分请求的表单字段。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).post('/user')
   [@HttpMultipart](/user/HttpMultipart)()
   FutureHttpResult<User> updateUser({
      @HttpFormField() required String username,
      @HttpFormField(name: 'pass') required String password,
   });

}

多部分请求也可以接受文件,使用 HttpFormField.file 注解。该注解的参数类型应该是 String,并且包含指向文件路径的值。

示例:

[@HttpService](/user/HttpService)()
class AppHttpService {

   [@HttpMethod](/user/HttpMethod).post('/user')
   [@HttpMultipart](/user/HttpMultipart)()
   FutureHttpResult<User> updateUser({
      @HttpFormField() required String username,
      @HttpFormField(name: 'pass') required String password,
      @HttpFormField.file() required String pathToFile,
   });

}

HTTP 服务

HTTP 服务可以嵌套,以提供更方便易读的代码。

[@HttpService](/user/HttpService)()
class AppHttpService {
   UsersHttpService get users;
}

[@HttpService](/user/HttpService)()
class UsersHttpService {
   [@HttpMethod](/user/HttpMethod).get('/user/:id')
   FutureHttpResult<User> getById([@PathParam](/user/PathParam)() int id);
}

...

final AppHttpService httpService = AppHttpService();
final HttpResult<User> result = await httpService.users.getById(11);

HTTP 服务也可以通过添加一个抽象方法 overrideHttpClient 来覆盖默认的 Dio 客户端。

[@HttpService](/user/HttpService)()
class UsersHttpService {
   UsersHttpService overrideHttpClient(Dio client);
}

HTTP 方法

HTTP 方法必须具有 FutureHttpResult<*> 的返回类型。

FutureHttpResultFuture<HttpResult<*>> 的快捷方式。

HttpResult 是一个密封类,由以下两种变体实现:

  • HttpResultData

    包含来自成功响应的解析数据。

  • HttpResultFailure

    包含从该请求抛出的异常和可选的堆栈跟踪。

HttpResult 提供辅助方法(whenmaybeWhen),这些方法接受基于结果的回调。

你还可以使用 asRecord 方法将结果转换为记录,格式为 (T?, Exception?, StackTrace?)

示例

查看 这里 的示例。

示例代码

import 'package:dio/dio.dart';
import 'package:example/jsonplaceholderapi.dart';
import 'package:example/models/order.dart';
import 'package:example/models/payment_method.dart';
import 'package:example/models/user.dart';
import 'package:http_client_plugin/http_client_plugin.dart';

part 'example.gen.dart';

[@HttpService](/user/HttpService)()
abstract class AppHttpService {
  AppHttpService._();

  factory AppHttpService(
    Dio client, [
    String pathPrefix,
  ]) = _$AppHttpServiceImpl;

  AppHttpService overrideHttpClient(Dio client);

  UsersService get user;

  PaymentMethodsService get paymentMethods;

  GooglePlacesService get places;

  JsonPlaceholderHttpService get jsonPlaceholder;
}

[@HttpService](/user/HttpService)('/users')
abstract class UsersService {
  UsersService._();

  factory UsersService(
    Dio client, [
    String pathPrefix,
  ]) = _$UsersServiceImpl;

  UsersService overrideHttpClient(Dio client);

  OrdersService get orders;

  [@HttpMethod](/user/HttpMethod).post('')
  FutureHttpResult<User> get([@QueryParam](/user/QueryParam)() int id);

  [@HttpMethod](/user/HttpMethod).post('/update')
  FutureHttpResult<User> updateUser({
    @HttpPayload() User data = const User(id: '11', username: '11'),
  });

  [@HttpMethod](/user/HttpMethod).post('/register')
  [@HttpMultipart](/user/HttpMultipart)()
  FutureHttpResult<User> register({
    @HttpFormField() required String username,
    @HttpFormField() required String password,
    @HttpFormField(name: 'user.name') required String name,
    @HttpFormField() required int age,
    @HttpFormField.file(name: 'picture') required String filePath,
  });
}

[@HttpService](/user/HttpService)('/:userId/orders')
abstract class OrdersService {
  OrdersService._();

  factory OrdersService(
    Dio client, [
    String pathPrefix,
  ]) = _$OrdersServiceImpl;

  OrdersService overrideHttpClient(Dio client);

  [@HttpMethod](/user/HttpMethod).get('')
  FutureHttpResult<Order> getOrderById({
    [@PathParam](/user/PathParam)() required int userId,
    [@QueryParam](/user/QueryParam)() required int id,
  });

  [@HttpMethod](/user/HttpMethod).get('/all')
  FutureHttpResult<List<Order>> getOrders([@PathParam](/user/PathParam)() int userId);
}

[@HttpService](/user/HttpService)('/payment-methods')
abstract class PaymentMethodsService {
  PaymentMethodsService._();

  factory PaymentMethodsService(
    Dio client, [
    String pathPrefix,
  ]) = _$PaymentMethodsServiceImpl;

  PaymentMethodsService overrideHttpClient(Dio client);

  [@HttpMethod](/user/HttpMethod).get('/:userId/list')
  FutureHttpResult<List<PaymentMethod>> getAll([@PathParam](/user/PathParam)() int userId);
}

enum GoogleApiResponseOutput {
  json,
  xml;

  [@override](/user/override)
  String toString() => name;
}

[@HttpService](/user/HttpService)('maps/api/place')
abstract class GooglePlacesService {
  GooglePlacesService._();

  factory GooglePlacesService(
    Dio client, [
    String pathPrefix,
  ]) = _$GooglePlacesServiceImpl;

  GooglePlacesService overrideHttpClient(Dio client);

  [@HttpMethod](/user/HttpMethod).get('/autocomplete')
  FutureHttpResult<Map<String, dynamic>> autocomplete({
    [@QueryParam](/user/QueryParam)() required String query,
    [@QueryParam](/user/QueryParam)() int limit = 25,
    [@QueryParam](/user/QueryParam)('apiKey') required int key,
    [@QueryParam](/user/QueryParam)('output') GoogleApiResponseOutput output = GoogleApiResponseOutput.json,
  });
}

void main() async {
  final Dio httpClient = Dio(BaseOptions(
    baseUrl: 'http://localhost:3000/api',
    connectTimeout: const Duration(seconds: 30),
    sendTimeout: const Duration(seconds: 30),
    receiveTimeout: const Duration(seconds: 30),
  ))
    ..interceptors.add(LogInterceptor());
  final AppHttpService httpService = AppHttpService(httpClient);

  // httpService.places 将使用不同的 HTTP 客户端
  httpService.places.overrideHttpClient(Dio(
    BaseOptions(baseUrl: 'https://maps.googleapis.com'),
  ));

  httpService.jsonPlaceholder.overrideHttpClient(Dio(
    BaseOptions(baseUrl: 'https://jsonplaceholder.typicode.com'),
  ));

  final HttpResult<User> getUserResult = await httpService.user.get(11);
  switch (getUserResult) {
    case HttpResultData<User>(:final User data):
      print(data);
    case HttpResultFailure<User>(:final Exception exception) when exception is DioException:
      print(exception);
    default:
      print('Something went bad');
  }

  final HttpResult<Post> getPost = await httpService.jsonPlaceholder.posts.getById(1);
  switch (getPost.asRecord()) {
    case (Post? post, _, _) when post != null:
      print('\nGot post');
      print(post);
    case (_, Exception? exception, _) when exception != null:
      print(exception);
  }
}

更多关于Flutter网络请求插件http_client_plugin的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络请求插件http_client_plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


http_client_plugin 是一个用于 Flutter 的网络请求插件,它提供了一种简单的方式来执行 HTTP 请求。虽然它可能不是 Flutter 社区中最流行的网络请求插件(如 httpdio),但它仍然是一个有用的工具,尤其是在你需要在项目中使用特定的网络请求功能时。

以下是如何在 Flutter 项目中使用 http_client_plugin 的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 http_client_plugin 的依赖:

dependencies:
  flutter:
    sdk: flutter
  http_client_plugin: ^latest_version

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 http_client_plugin

import 'package:http_client_plugin/http_client_plugin.dart';

3. 创建 HTTP 客户端实例

你可以通过 HttpClientPlugin 类创建一个 HTTP 客户端实例:

final httpClient = HttpClientPlugin();

4. 执行 HTTP 请求

使用 httpClient 实例来执行 HTTP 请求。例如,执行一个 GET 请求:

void fetchData() async {
  try {
    final response = await httpClient.get('https://jsonplaceholder.typicode.com/posts');
    if (response.statusCode == 200) {
      print('Data fetched successfully: ${response.body}');
    } else {
      print('Failed to load data: ${response.statusCode}');
    }
  } catch (e) {
    print('Error: $e');
  }
}

5. 处理响应

你可以通过 response.body 访问响应体,并通过 response.statusCode 获取状态码。

6. 其他 HTTP 方法

http_client_plugin 还支持其他 HTTP 方法,如 POST、PUT、DELETE 等。例如,执行一个 POST 请求:

void postData() async {
  try {
    final response = await httpClient.post(
      'https://jsonplaceholder.typicode.com/posts',
      body: {
        'title': 'foo',
        'body': 'bar',
        'userId': 1,
      },
    );
    if (response.statusCode == 201) {
      print('Data posted successfully: ${response.body}');
    } else {
      print('Failed to post data: ${response.statusCode}');
    }
  } catch (e) {
    print('Error: $e');
  }
}

7. 处理错误

在请求过程中,可能会遇到网络错误或其他异常。你可以使用 try-catch 块来捕获并处理这些错误。

8. 配置请求

你可以在请求中添加 headers、query 参数等。例如:

void fetchDataWithHeaders() async {
  try {
    final response = await httpClient.get(
      'https://jsonplaceholder.typicode.com/posts',
      headers: {
        'Authorization': 'Bearer your_token',
      },
    );
    if (response.statusCode == 200) {
      print('Data fetched successfully: ${response.body}');
    } else {
      print('Failed to load data: ${response.statusCode}');
    }
  } catch (e) {
    print('Error: $e');
  }
}

9. 关闭客户端

在不再需要使用 HTTP 客户端时,可以关闭它以释放资源:

httpClient.close();
回到顶部