2、Dio库入门:Dio的安装与配置、基本GET/POST请求、请求与响应拦截器初探

2.1 为什么选Dio?

在Flutter的网络请求领域,Dio几乎是事实标准。我刚开始做Flutter项目时,也试过用原生的http包,但很快就发现一个问题——代码重复率太高了。每个接口都要写请求头、处理超时、解析错误码……说白了,就是体力活。

Dio帮我们把这些脏活累活都干了。它提供了拦截器、全局配置、文件上传下载、请求取消等能力。你想想看,一个App里少说几十个接口,如果每个都单独配置,那维护成本得多高?

2.2 安装与配置

安装Dio很简单。在pubspec.yaml里加上依赖就行:

dependencies:
  dio: ^5.4.0

然后执行flutter pub get。嗯,这里要注意版本号,我建议用最新的稳定版,别追太新的beta版,容易踩坑。

接下来是初始化。我个人习惯把Dio实例封装成一个单例,这样全局共享一套配置:

class HttpClient {
  late final Dio _dio;

  HttpClient._internal() {
    _dio = Dio(BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: const Duration(seconds: 10),
      receiveTimeout: const Duration(seconds: 10),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ));
  }

  static final HttpClient _instance = HttpClient._internal();
  factory HttpClient() => _instance;

  Dio get dio => _dio;
}

这里有几个关键配置:

  • baseUrl:基础地址,后面写接口路径时只用写相对路径
  • connectTimeout:连接超时,我一般设10秒
  • receiveTimeout:接收超时,也是10秒
  • headers:公共请求头,比如Content-Type
小技巧:超时时间别设太短。我在项目中遇到过用户网络差的情况,5秒超时经常失败,后来改成10秒就好多了。

2.3 基本GET请求

GET请求是最常用的。比如获取用户列表:

Future<List<User>> getUsers() async {
  try {
    final response = await HttpClient().dio.get('/users');
    if (response.statusCode == 200) {
      final List<dynamic> data = response.data;
      return data.map((json) => User.fromJson(json)).toList();
    }
    throw Exception('请求失败: ${response.statusCode}');
  } on DioException catch (e) {
    // 这里处理网络异常
    throw Exception('网络错误: ${e.message}');
  }
}

带参数的GET请求也很常见:

final response = await dio.get(
  '/users',
  queryParameters: {'page': 1, 'size': 20},
);

你看,Dio会自动把queryParameters拼接到URL后面,省得我们自己拼字符串了。

2.4 基本POST请求

POST请求用于提交数据。比如创建新用户:

Future<User> createUser(String name, String email) async {
  try {
    final response = await dio.post(
      '/users',
      data: {
        'name': name,
        'email': email,
      },
    );
    return User.fromJson(response.data);
  } on DioException catch (e) {
    throw Exception('创建用户失败: ${e.message}');
  }
}

这里有个细节——data参数传Map,Dio会自动序列化成JSON。如果你传的是自定义对象,记得先调用toJson()方法。

注意:POST请求的Content-Type默认是application/json。如果你要传表单数据,需要手动设置:options: Options(contentType: 'application/x-www-form-urlencoded')

2.5 请求拦截器初探

拦截器是Dio最强大的功能之一。它允许你在请求发出前或响应返回后插入自定义逻辑。

先看请求拦截器。我经常用它来统一添加Token:

dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 从本地存储获取Token
    final token = LocalStorage.getToken();
    if (token != null) {
      options.headers['Authorization'] = 'Bearer $token';
    }
    // 打印请求日志
    print('请求路径: ${options.path}');
    print('请求参数: ${options.queryParameters}');
    handler.next(options);
  },
));

为什么要用拦截器?你想想看,如果每个接口都手动加Token,万一哪天Token的key变了,你得改多少个地方?用拦截器就改一处,省心。

我曾经接手过一个老项目,里面每个请求都手动拼Token,结果后端把Authorization改成了token字段……嗯,那两天我改代码改到想吐。

2.6 响应拦截器初探

响应拦截器用来统一处理返回数据。比如全局错误码处理:

dio.interceptors.add(InterceptorsWrapper(
  onResponse: (response, handler) {
    // 假设后端返回格式: { code: 0, data: {}, message: '成功' }
    final code = response.data['code'];
    if (code != 0) {
      // 业务错误,抛出异常
      throw DioException(
        requestOptions: response.requestOptions,
        response: response,
        message: response.data['message'] ?? '未知错误',
      );
    }
    // 只返回data部分
    response.data = response.data['data'];
    handler.next(response);
  },
  onError: (error, handler) {
    // 统一处理网络错误
    if (error.type == DioExceptionType.connectionTimeout) {
      // 连接超时,提示用户
      print('连接超时,请检查网络');
    } else if (error.type == DioExceptionType.badResponse) {
      // HTTP状态码错误
      print('服务器错误: ${error.response?.statusCode}');
    }
    handler.next(error);
  },
));

这样做的好处很明显——业务代码里不用再写一堆if-else判断错误码了。拦截器帮你过滤干净,业务层拿到的就是干净的数据。

核心思路:拦截器就像流水线上的质检员。请求拦截器负责给包裹贴上标签(Token),响应拦截器负责拆掉包装(统一处理错误码),业务层拿到的是可以直接用的成品。

2.7 拦截器的执行顺序

多个拦截器是按添加顺序执行的。请求时从上到下,响应时从下到上。说白了就是先进先出:

步骤 操作 说明
1 添加Token拦截器 请求前注入Token
2 添加日志拦截器 请求前打印日志
3 发送请求 实际网络请求
4 日志拦截器响应 响应后打印结果
5 Token拦截器响应 响应后处理Token刷新

这个顺序很重要。比如你想在日志里看到完整的请求信息,那日志拦截器就得加在Token拦截器后面,这样打印时Token已经注入进去了。

2.8 小结

这一章我们走通了Dio的安装配置、基本请求和拦截器入门。说实话,这些只是Dio的冰山一角。但有了这个基础,后面讲请求取消、文件上传、Cookie管理这些高级功能时,你就不会觉得陌生了。

下一章我们会深入拦截器的进阶用法——比如自动刷新Token、请求重试、缓存策略。这些才是真正提升开发效率的利器。

课后练习:试着写一个日志拦截器,把每次请求的URL、耗时、返回状态码都打印出来。你会发现调试接口时特别有用。