likes
comments
collection
share

Flutter3.0 仿微博瀑布流 - 网络请求封装

作者站长头像
站长
· 阅读数 107

项目地址

github.com/XinleZhou20…

有疑问请关注公众号 fullstack2022,答疑。


使用Dio封装网络请求模块

自上一节,我们讲清如何使用GetX构建清晰的项目MVC结构后,这一篇将向大家展示如何利用Dio创建封装网络请求类,并展示如何处理网络请求结果(包括异常)。 首先在pubspec.yaml中引入Dio插件(完整的插件清单请见该专栏的第一篇文章):

pubspec.yaml

dependencies:
    dio:

Dio初始化

首先我们在lib/core/network/core目录下新建dio_builder.dart文件。目录结构如下:

Flutter3.0 仿微博瀑布流 - 网络请求封装

首先我们新建一个Dio对象,项目中多有的网络请求都是由这个对象发起的:

Dio? dio;

随后我们新建一个DioBuilder类,用来初始化上面的dio实例。首先在DioBuilder类中新建如下几个变量:

  DioBuilder.dart
  
  class DioBuilder {
  
      BaseOptions? options;

      String protocol = "http";

      String baseUrl = "localhost";

      Map<String, dynamic> headers = {};

      int connectTimeout = 60 * 1000;

      List<Interceptor> interceptors = [];
  ...
  }

我们来对这6个变量逐一解释。

  1. BaseOptions? options; BaseOptions是设置Dio的基本属性,包括baseUrl(服务器基本请求地址,一般是ip+端口号), connectTimeout(网络请求超时时间), responseType(返回结果类型), headers(请求头)等等。我们将在下面详细介绍。

  2. String protocol = "http"; 这个属性是定义请求类型,通常是http, https, ftp等。该项目,我们使用http

  3. String baseUrl = "localhost"; 服务器基本请求地址,一般是ip+端口号。这里给它一个默认值是本机的localhost

  4. Map<String, dynamic> headers = {}; headers是定义请求头的属性,我们一般将接口的签名,手机设备的型号等信息放在请求头中,这个项目中并没有使用到。

  5. int connectTimeout = 60 * 1000; 网络请求超时时间,单位是毫秒。网络请求超时后将进入网络请求的异常处理中。

  6. List<Interceptor> interceptors = []; 网络请求的拦截器,我们一般会在这个拦截器中对每个请求添加header请求头信息,以及可以在拦截器中进行统一的请求结果处理(包括异常)。

变量定义完成之后,我们来定义它们的Setter方法:

  DioBuilder.dart
  
  class DioBuilder {
  ...
      DioBuilder setProtocol(String protocolType) {
        protocol = protocolType;
        return this;
      }

      DioBuilder setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        return this;
      }

      DioBuilder setHeaders(Map<String, dynamic> headers) {
        headers.forEach((String key, dynamic value) {
        this.headers[key] = value;
        });
        return this;
      }

      DioBuilder setConnectTimeout(int connectTimeout) {
        this.connectTimeout = connectTimeout;
        return this;
      }

      DioBuilder addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
        return this;
      }
  }

这里的Setter方法是有返回值的,返回值是DioBuilder类对象。是为了方便连续设置DioBuilder的属性。我们将在下文中讲解如何初始化DioBuilder

然后新建一个build()方法把Dio对象初始化出来。

DioBuilder.dart

  class DioBuilder {
    ...
    build() {
        options = BaseOptions(
            /// 服务器基本地址
            baseUrl: "$protocol://$baseUrl", 
            /// 请求超时时间
            connectTimeout: connectTimeout,

            /// 返回数据类型,这里服务端返回的是json格式数据
            responseType: ResponseType.json,

            /// 服务器响应超时时间
            receiveTimeout: connectTimeout,
            /// 请求头
            headers: headers,
            /// 内容类型
            contentType: Headers.jsonContentType);
        
        /// 初始化Dio对象
        dio = Dio(options);
        
        /// 拦截器
        interceptors.add(BaseInterceptor());
        
        /// Dio对象添加拦截器
        for (Interceptor element in interceptors) {
          dio!.interceptors.add(element);
        }
      }
  }

上面的初始化Dio对象的注释已经很详细了。需要注意的一点是添加拦截器的操作,我们将在下文中叙述。

随后我们便可以调用上述的_build()方法进行初始化。这个调用步骤我们放在main.dart中:

main.dart

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  ...

  _buildDio(ApiUtil.DEBUG_MODE);

  runApp(createApp());
}

void _buildDio(bool debug) async {
  DioBuilder? dioBuilder;

  if (debug) {
    dioBuilder = DioBuilder()
        .setProtocol('http')
        .setBaseUrl(ApiUtil.DEBUG_HOST);
  } else {
    dioBuilder = DioBuilder()
        .setProtocol('http')
        .setBaseUrl(ApiUtil.ONLINE_HOST);
  }

  dioBuilder.build();
}

调用的代码一目了然!这里需要注意的是在初始化Dio对象的时候使用了ApiUtil.DEBUG_MODE这个常量来区分当前的环境,我们的环境分为开发环境和生产环境,这很符合我们现实开发的需要,我们经常需要在开发环境和生产环境中进行切换。


网络请求公共属性类

上述的ApiUtil.DEBUG_MODE便是在网络请求公共属性类中定义的,这个类的文件名叫做api_util.dart

api_util.dart

/// APP 接口 及 服务器地址配置类
class ApiUtil {
  static const DEBUG_MODE = false;    // 开发环境和生产环境的切换开关
  static const LOG_DEBUG_MODE = true; // 日志log是否打开的开关

  static const PROTOCOL = "http://";

  static const API = "/weibo";

  /// 开发环境
  static const DEBUG_HOST = "172.16.41.69:8088";
  
  /// 生产环境
  static const ONLINE_HOST = "150.158.96.11";
  
  
  /// 具体的接口名定义
  /// 用户微博流接口
  static const USER_TIMELINE = "/queryWeibo";
}

上述的注释也是一目了然!例如我们请求用户微博流接口,我们请求地址就是PROTOCOL+baseUrl + API + USER_TIMELINEhttp://172.16.41.69:8088/weibo/queryWeibo


网络请求拦截器

我们可以在拦截器中对每个请求添加header请求头信息,以及可以在拦截器中进行统一的请求结果处理(包括异常)。 上述Dio对象在初始化时,我们给Dio对象设置了一个统一的请求头header对象。如果我们有这样的需求:一个特殊的API接口,我们需要添加特殊的请求头该如何实现?这便要借助我们的网络请求拦截器了。 我们新建一个interceptor.dart文件,在其中新建一个BaseInterceptor类。 我们需要对特殊的接口添加特殊的请求头,那么就需要在BaseInterceptor类中对每一次http请求进行拦截,如何拦截? 让BaseInterceptor继承InterceptorsWrapper类,并重写其中的onRequest方法。注释说的很清楚了!

interceptor.dart

class BaseInterceptor extends InterceptorsWrapper {
  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    /// 添加请求头,可以使用options.path对每一个请求进行区分
    options.headers = await RequestHeader.generateHeader();

    super.onRequest(options, handler);
  }
...

拦截器不仅可以对请求发出时进行拦截,也可以在服务端结果返回后进入具体的业务处理类之前进行拦截。所以我们在BaseInterceptor中,可以对返回结果进行处理,该项目仅仅是对服务端返回的http status code等于200http status code不等于200进行区分。

  • http status code等于200进入业务正常处理流程。
  • http status code不等于200进入业务异常处理流程。
interceptor.dart

class BaseInterceptor extends InterceptorsWrapper {
    ...
      @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
   // 等于200
    return handler.next(response); // continue
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
      // 非200 服务器错误都会走到这
     super.onError(err, handler);
  }
}

至此,我们的网络请求封装类便完成了,我们将在接下来的章节中,使用该封装类进行一次完整的网络请求。

转载自:https://juejin.cn/post/7128279065542262798
评论
请登录