Flutter3.0 仿微博瀑布流 - 网络请求封装
项目地址
有疑问请关注公众号 fullstack2022,答疑。
使用Dio封装网络请求模块
自上一节,我们讲清如何使用GetX
构建清晰的项目MVC
结构后,这一篇将向大家展示如何利用Dio
创建封装网络请求类,并展示如何处理网络请求结果(包括异常)。
首先在pubspec.yaml
中引入Dio
插件(完整的插件清单请见该专栏的第一篇文章):
pubspec.yaml
dependencies:
dio:
Dio初始化
首先我们在lib/core/network/core
目录下新建dio_builder.dart
文件。目录结构如下:
首先我们新建一个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个变量逐一解释。
-
BaseOptions? options;
BaseOptions
是设置Dio
的基本属性,包括baseUrl
(服务器基本请求地址,一般是ip+端口号),connectTimeout
(网络请求超时时间),responseType
(返回结果类型),headers
(请求头)等等。我们将在下面详细介绍。 -
String protocol = "http";
这个属性是定义请求类型,通常是http
,https
,ftp
等。该项目,我们使用http
。 -
String baseUrl = "localhost";
服务器基本请求地址,一般是ip
+端口号
。这里给它一个默认值是本机的localhost
。 -
Map<String, dynamic> headers = {};
headers
是定义请求头的属性,我们一般将接口的签名,手机设备的型号等信息放在请求头中,这个项目中并没有使用到。 -
int connectTimeout = 60 * 1000;
网络请求超时时间,单位是毫秒。网络请求超时后将进入网络请求的异常处理中。 -
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_TIMELINE
。
http://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
等于200
和http 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