Flutter网络请求插件http_api的使用

发布于 1周前 作者 wuwangju 来自 Flutter

Flutter网络请求插件http_api的使用

概述

http_api 是一个简单而强大的 http 包的封装。它提供了拦截器(中间件)支持、响应缓存支持,并且与 Flutter 集成良好。

特点

  • 拦截器 / 中间件:允许在请求前和响应后执行特定任务。
  • FormData 支持:方便处理文件上传等表单数据。
  • 缓存支持:避免不必要的网络请求,提升用户体验。
  • 易于扩展和自定义:可以根据需要轻松扩展功能。
  • 跨平台:适用于 Dart 和 Flutter 项目。

重要提示

该库仍在开发中,可能会有破坏性的 API 变更。如果要使用此库,请确保指定要使用的版本,例如:

dependencies:
  http_api: 0.7.2+2

入门指南

1. 创建你的 Api 类

通过继承 BaseApi 类来创建你的 Api 类。

// 定义你的 api 类
class Api extends BaseApi {

  /// 提供 BaseApi 构造函数所需的数据
  Api(Uri url) : super(url);

  /// 实现 api 请求方法
  Future<PostModel> getPostById(int id) async {

    /// 使用 [send] 方法发起 api 请求
    final response = await send(ApiRequest(
      endpoint: "/posts/$id",
    ));

    /// 处理返回的数据
    /// 例如:将 HTTP 响应解析为模型对象
    return PostModel.fromJson(response.body);
  }
}

2. 使用你的 Api 类

void main() async {
  /// 定义 api 基础 URL
  final url = Uri.parse("https://example.com/api");

  /// 创建 api 实例
  final api = Api(url);
  
  /// 发起请求
  Post post = await api.getPostById(10);

  /// 处理返回的数据
  print(post.title);

  /// 释放资源
  api.dispose();

  /// 现在你已经准备好摇滚了!
}

拦截器 / 中间件

拦截器允许你在请求前和响应后执行某些任务。

添加拦截器

通过构造函数的 link 参数提供拦截器。

void main() {
  Api(
    Uri.parse("https://example.com/api"),
    /// 分配拦截器(可以通过链式调用提供多个拦截器)
    link: AuthLink()
        .chain(LoggerLink(responseBody: true))
        .chain(HttpLink()),
  );
}

拦截器的工作原理

当你通过 send(或 cacheAndNetworkcacheIfAvailable)发起请求时,请求会经过拦截器链,最终到达 HttpLink,由 HttpLink 发起实际的 HTTP 请求。然后所有拦截器会依次接收到 API 响应,最后返回到 send 方法的调用处。

                                 __________               ____________               __________
calling send()    |  -request-> | AuthLink |  -request-> | LoggerLink |  -request-> | HttpLink |
send returns data | <-response- |__________| <-response- |____________| <-response- |__________|
                                    tasks                     tasks                 http request   

自定义拦截器

你可以通过继承 ApiLink 类来创建自定义拦截器。

/// 创建链接类并继承 [ApiLink]。
class CustomLink extends ApiLink {

  /// 覆盖 next 方法。
  @override
  Future<ApiResponse> next(ApiRequest request) async {
    /// 在发送 HTTP 请求之前执行的操作
    /// 例如:测量请求耗时
    final requestTime = DateTime.now();

    /// 调用 super.next 触发下一个拦截器
    ApiResponse response = await super.next(request);

    /// 在接收到 API 响应后执行的操作
    /// 例如:打印请求耗时
    final requestDuration = DateTime.now().difference(requestTime);
    print(
      "Request ${request.id} duration: "
      "${requestDuration.inMilliseconds} ms",
    );

    /// 返回响应
    return response;
  }
}

如果你的拦截器需要在 API 实例销毁时释放资源,可以覆盖其 dispose 方法。

对于简单的拦截器,创建一个继承 ApiLink 的类可能有些过度设计。因此,你也可以在调用时直接定义拦截器:

ApiLink.next((ApiRequest request, NextFunction next) async {
  final requestTime = DateTime.now();

  final response = await next(request);
  
  final requestDuration = DateTime.now().difference(requestTime);

  print(
    "Request ${request.id} duration: "
    "${requestDuration.inMilliseconds} ms",
  );

  return response;
});

缓存

http_api 支持响应缓存,以避免不必要的网络请求或提升用户体验。

添加缓存

  1. 在你的 Api 类中添加 Cache 混合。
- class Api extends BaseApi {
+ class Api extends BaseApi with Cache {
  1. 提供一个缓存管理器。
class Api extends BaseApi with Cache {

  /// 提供一个缓存管理器
  /// 该库提供了一个内存缓存管理器实现
  @override
  CacheManager createCacheManager() => InMemoryCache();

  /// ** 你的自定义 Api 类实现 **
}
  1. 完成!

现在你可以利用响应缓存了。

缓存混合

缓存混合为你的 API 实例添加了 cacheAndNetworkcacheIfAvailable 方法,以及可通过 cache 属性访问的缓存管理器。

默认情况下,每个包含 key 参数且响应成功的请求会自动更新缓存。你可以通过覆盖 shouldUpdateCache 方法来决定哪些响应应该保存到缓存中。

cacheIfAvailable

从缓存中检索响应,如果没有则回退到网络请求。返回类型为 Future<ApiResponse>

cacheAndNetwork

先从缓存中检索响应,如果可用则返回缓存响应,否则发起网络请求。返回类型为 Stream<ApiResponse>

示例:

// Api.dart
class Api extends BaseApi with Cache {

  @override
  CacheManager createCacheManager() => InMemoryCache();

  Stream<PostModel> getPostById(int id) {
    Stream<ApiResponse> request = ApiRequest(
      /// 缓存键是必需的
      /// 响应将根据此键缓存和检索
      key: CacheKey("posts/$id"),
      endpoint: "/posts/${id}",
    );
    
    /// 因为 `cacheAndNetwork` 方法返回 Stream,我们可以利用其所有特性
    /// 例如:将响应转换为模型对象
    final transformResponseToPostModel =
        StreamTransformer<ApiResponse, PostModel>.fromHandlers(
      handleData: (response, sink) => sink.add(
        PostModel.fromJson(response.body),
      ),
    );

    /// 返回转换后的流
    return cacheAndNetwork(request)
        .transform(transformResponseToPostModel);
  }

  /// ** 你的自定义 Api 类实现 **
}

cache

cache 属性包含一个通过 createCacheManager 方法内部创建的 CacheManager 实例。通过此属性,你可以手动操作缓存。

示例:

/// 将新响应保存到缓存并检索之前保存的响应
ApiResponse saveResponseToCache(CacheKey key, ApiResponse response) async {
  final oldResponse = await api.cache.read(key);
  await api.cache.write(key, response);
  return oldResponse;
}

shouldUpdateCache

决定是否将与请求相关的响应保存到缓存中。

默认实现:

bool shouldUpdateCache(ApiRequest request, ApiResponse response) {
 return request.key != null && response.ok;
}

FormData

处理表单数据,包括文件上传。

示例:

/// 创建 [FormData] 实例
final formData = FormData();

/// 添加字段
formData.append('photo[visible]', true);
formData.append('photo[type]', 'image/png');
formData.append('photo[size]', 1920);

/// 添加文件
File file = getImageFile();
formData.appendFile('photo[file]', file, filename: "my_filename.png");

/// 通过 api 类发送文件
final response = await api.post("/photos/upload", body: formData);

http_api 和 Flutter ♥️

http_api 包与 Dart 和 Flutter 项目都兼容良好。

提示

你可以使用 provider 包将你的 Api 实例提供给 widget 树中的其他组件。

示例:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider(
      create: (_) => Api(
        /// 提供基础 URL
        url: Uri.parse("https://example.com/api"),

        /// 分配中间件(可以通过链式调用提供多个中间件)
        link: LoggerLink(responseDuration: true, endpoint: true)
            .chain(HttpLink()),
      ),
      child: MaterialApp(
        title: 'http_api example',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        initialRoute: "home",
        routes: {
          "basic": (_) => BasicExample(),
          "cache": (_) => CacheExample(),
          "home": (_) => HomeScreen(),
        },
      ),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('http_api examples'),
      ),
      body: SafeArea(
        child: SizedBox(
          width: double.infinity,
          child: ListView(
            padding: EdgeInsets.all(15),
            children: <Widget>[
              ElevatedButton(
                onPressed: () => Navigator.of(context).pushNamed("basic"),
                child: Text("Basic example"),
              ),
              ElevatedButton(
                onPressed: () => Navigator.of(context).pushNamed("cache"),
                child: Text("Cache example"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

TODO

  • 改进文档
    • 改进 README 文件
  • 添加开发工具

希望这些信息对你有所帮助!如果你有任何问题或需要进一步的帮助,请随时提问。


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

1 回复

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


当然,以下是一个关于如何在Flutter中使用http插件进行网络请求的示例代码。虽然你提到的是http_api,但Flutter社区中更广泛使用的插件是http包。如果http_api是一个特定项目或公司内部的封装,代码结构可能会有所不同,但核心思想类似。以下是使用http插件的示例:

首先,确保你的pubspec.yaml文件中已经添加了http依赖:

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3  # 请检查最新版本号并更新

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

接下来,在你的Dart文件中,你可以这样使用http插件进行网络请求:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _responseData = '';

  void _makeRequest() async {
    final url = 'https://api.example.com/data'; // 替换为你的API URL

    try {
      final response = await http.get(Uri.parse(url));

      if (response.statusCode == 200) {
        // 如果服务器返回200 OK响应码,解析JSON数据
        setState(() {
          _responseData = jsonDecode(response.body).toString();
        });
      } else {
        // 如果响应码不是200,显示错误信息
        setState(() {
          _responseData = 'Error: Failed to load data\nStatus Code: ${response.statusCode}';
        });
      }
    } catch (e) {
      // 捕获并显示任何异常
      setState(() {
        _responseData = 'Error: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter HTTP Request Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Response Data:',
              style: TextStyle(fontSize: 20),
            ),
            SizedBox(height: 20),
            Expanded(
              child: SingleChildScrollView(
                child: Text(
                  _responseData,
                  style: TextStyle(fontSize: 16),
                ),
              ),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _makeRequest,
              child: Text('Fetch Data'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个按钮用于触发网络请求。当按钮被点击时,_makeRequest函数会被调用,该函数使用http.get方法向指定的URL发送GET请求。如果请求成功(即HTTP状态码为200),则解析返回的JSON数据并显示在屏幕上;否则,显示一个错误消息。

请注意,这里的URL(https://api.example.com/data)需要替换为你实际要请求的API URL。此外,根据API返回的数据格式,你可能需要调整解析JSON数据的代码。

这个示例展示了如何使用http插件进行基本的网络请求和错误处理。在实际应用中,你可能还需要处理更多复杂的情况,比如POST请求、请求头设置、身份验证等。

回到顶部