封装网络库考虑的几个方面:
- 请求参数的封装:将请求所需的参数进行封装,例如 URL、请求头、请求体等。可以定义一个统一的数据结构或模型类来表示请求参数,以便于传递和管理。
- 响应结果的封装:将网络请求返回的响应结果进行封装,例如状态码、响应头、响应体等。同样,可以定义一个统一的数据结构或模型类来表示响应结果,以便于处理和解析。
- 错误处理的封装:对网络请求可能出现的错误进行封装和处理,例如网络连接失败、超时、服务器错误等。可以定义自定义的异常类或错误码,以及相应的错误处理机制。
- 拦截器的封装:如果第三方网络库支持拦截器机制,你可以封装拦截器,用于在请求发起前或响应返回后进行额外的处理,如添加身份验证、日志记录等。
响应结果封装
- 响应状态码:获取响应的HTTP状态码,以判断请求是否成功或出现错误。常见的状态码有200表示成功,4xx表示客户端错误,5xx表示服务器错误等。
- 响应头(Headers):获取响应的头部信息,如Content-Type、Authorization等。根据接口要求和需要,提取并封装相应的响应头信息。
- 响应体(Body):获取响应的主体数据,通常是服务器返回的具体内容。根据接口要求和数据类型,将响应体进行适当的解析和处理,可以是JSON、文本、二进制数据等。
- 错误处理:根据响应状态码或其他错误标识,判断响应是否包含错误信息。如果存在错误,需要将错误信息进行封装和处理,以便在前端进行展示或进一步处理。
- 数据转换:根据接口返回的数据类型和需求,可能需要对响应数据进行适当的转换和格式化,以便在前端进行处理和展示。
- 异常处理:处理网络请求过程中可能出现的异常情况,如网络错误、超时等。提供友好的提示或错误处理机制,确保用户能够得到合适的反馈。
错误处理封装
- 错误码(Error Code):定义一套错误码,用于标识不同类型的错误。每个错误码应具有唯一性,并且可以根据错误码快速定位和处理错误。
- 错误信息(Error Message):为每个错误码提供相应的错误信息,以便在错误发生时能够提供清晰的错误描述。错误信息应该简明扼要、易于理解,并且能够帮助开发者或用户快速定位问题。
- 异常处理机制:针对可能出现的异常情况,如网络连接失败、请求超时等,需要实现相应的异常处理机制。这可以包括使用try-catch语句捕获异常、设置超时时间、重试机制等。
- 错误回调或异常抛出:根据具体情况,可以选择将错误通过回调函数返回给调用方,或者抛出异常供上层代码捕获和处理。这取决于项目的架构和开发者的偏好。
- 友好的错误提示:对于用户可见的错误,如接口调用失败或数据获取错误,需要提供友好的错误提示,以便用户能够理解并采取适当的操作。
- 日志记录:在错误处理中,建议记录相关的错误日志,包括错误码、错误信息、请求参数等,以便进行故障排查和问题定位。
默认配置项
我们在使用第三方网络库时,它会有相应的默认配置项的,配置好默认配置项时,为后面请求省了很多事。
那么常见的默认网络库配置项有如下:
- 请求方法(HTTP Method):通常默认为 GET 方法,用于获取资源。其他常见的请求方法包括 POST、PUT、DELETE 等。
- 请求超时时间(Timeout):默认情况下,网络库会设置一个请求超时时间,即在指定时间内没有收到服务器响应,则认为请求超时。默认超时时间通常为几秒钟,可以根据需要进行调整。
- 请求头(Headers):默认情况下,网络库不会添加任何请求头。你可以自定义请求头,例如添加身份验证信息、指定数据格式等。
- 响应数据格式(Response Format):网络库通常默认将响应数据解析为特定的格式,如 JSON、XML 或原始文本。你可以根据需要进行解析或更改默认格式。
- 连接池(Connection Pooling):一些网络库可能会使用连接池来管理与服务器的连接。连接池允许重复使用已建立的连接,以提高性能和效率。
本文会 讲Flutter dio
默认配置项:
dio
BaseOptions
配置项源码:
BaseOptions({ String? method, // 请求方法,默认为null,表示使用库的默认请求方法 Duration? connectTimeout, // 连接超时时间,默认为null,表示使用库的默认超时时间 Duration? receiveTimeout, // 接收超时时间,默认为null,表示使用库的默认超时时间 Duration? sendTimeout, // 发送超时时间,默认为null,表示使用库的默认超时时间 String baseUrl = '', // 基础URL,默认为空字符串 Map<String, dynamic>? queryParameters, // 查询参数,默认为null Map<String, dynamic>? extra, // 额外配置参数,默认为null Map<String, dynamic>? headers, // 请求头,默认为null ResponseType? responseType = ResponseType.json, // 响应数据格式,默认为ResponseType.json,表示解析为JSON格式 String? contentType, // 请求的Content-Type,默认为null bool Function(int?)? validateStatus, // 自定义验证响应状态码的函数,默认为null bool? receiveDataWhenStatusError, // 当响应状态码错误时是否继续接收数据,默认为null bool? followRedirects, // 是否跟随重定向,默认为null int? maxRedirects, // 最大重定向次数,默认为null bool? persistentConnection, // 是否保持持久连接,默认为null List<int> Function(String, RequestOptions)? requestEncoder, // 请求编码器,默认为null String? Function(List<int>, RequestOptions, ResponseBody)? responseDecoder, // 响应解码器,默认为null ListFormat? listFormat, // JSON数组的解析格式,默认为null })
Demo
import 'package:dio/dio.dart'; class HttpApi { // 配置基本的请求选项 static final BaseOptions options = BaseOptions( baseUrl: 'http://localhost:3001/', // 设置请求的基础 URL method: 'GET', //默认请求方法 connectTimeout: const Duration(seconds: 5), // 设置连接超时时间为 5 秒 receiveTimeout: const Duration(seconds: 3), // 设置接收超时时间为 3 秒 headers: { 'User-Agent': 'Dio', // 设置请求头信息 }, ); static Dio dio = Dio(options); }
拦截器
在Flutter中,Dio是一个强大的HTTP客户端库,它提供了拦截器(Interceptors)机制,用于在发送请求和接收响应之前进行额外的处理。拦截器可以用于添加身份验证、日志记录、错误处理等功能。
对于每个 dio 实例,我们可以添加一个或多个拦截器,通过这些拦截器,我们可以在处理请求、响应和错误之前拦截它们或 catchError。
内置拦截器
有的网路库它会内置一些拦截器,下面是 dio
内置的拦截器:
LogInterceptor
:用于打印请求和响应的日志信息。它可以帮助开发者调试和监控网络请求,包括请求方法、URL、请求头、请求体、响应状态码、响应头和响应体等。InterceptorsWrapper
:这是一个包装类拦截器,它可以同时包含多个拦截器,并按照添加的顺序依次执行。通过使用InterceptorsWrapper,可以方便地组合多个拦截器,实现复杂的拦截器逻辑
List<Interceptor> inters = []; inters.add(LogInterceptor()); //添加内置拦截器 dio.interceptors.addAll(inters);
自定义拦截器
自定义过滤器需要继承 Dio
的Interceptor
类并重写其方法来实现的。
在自定义拦截器中,我们可以在onRequest方法中处理请求前的逻辑,在onResponse方法中处理接收到响应后的逻辑,在onError方法中处理请求发生错误时的逻辑。
// 自定义拦截器 class CustomInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { // 在发送请求前的处理逻辑 print('Request Interceptor - Request: ${options.uri}'); print('我发送了请求啦--------'); handler.next(options); // 继续执行下一个拦截器或发送请求 } @override void onResponse(Response response, ResponseInterceptorHandler handler) { // 在接收到响应后的处理逻辑 print('Request Interceptor - Response: ${response.statusCode}'); print('我收到请求啦--------'); handler.next(response); // 继续执行下一个拦截器或返回响应 } @override // ignore: deprecated_member_use void onError(DioError err, ErrorInterceptorHandler handler) { // 在请求发生错误时的处理逻辑 print('Request Interceptor - Error: ${err.message}'); handler.next(err); // 继续执行下一个拦截器或抛出错误 } }
发起请求
上面我已经把网络库的 默认配置项,拦截器都配置好了,接下来就是开始发起请求了。我可以把发起请求做个简单封装,把可变的参数提供去,这样每次再发起请求时,只需要传递参数就可以了。
try { Response response = await dio.request<T>(url, queryParameters: params, options: options); return response.data; } catch (e) { print(e); return Future.error(e); }
完整封装代码
/* * @Author: HaiJun * @Date: 2023-08-15 14:13:54 * @LastEditTime: 2023-08-16 14:29:43 * @FilePath: \flutter_demo_show\lib\utils\http.dart * @Description: * */ import 'package:dio/dio.dart'; import './CustomInterceptor.dart'; class HttpApi { // 配置基本的请求选项 static final BaseOptions options = BaseOptions( baseUrl: 'http://localhost:3001/', // 设置请求的基础 URL method: 'GET', //默认请求方法 connectTimeout: const Duration(seconds: 5), // 设置连接超时时间为 5 秒 receiveTimeout: const Duration(seconds: 3), // 设置接收超时时间为 3 秒 headers: { 'User-Agent': 'Dio', // 设置请求头信息 }, ); static Dio dio = Dio(options); static Future<T> request<T>(String url, {String method = "get", Map<String, dynamic>? params}) async { // 1请求的单独配置 final options = Options(method: method); // 2 添加一个拦截器 Interceptor inter = InterceptorsWrapper( onRequest: (options, handler) { return handler.next(options); }, onResponse: (e, handler) { print(e); return handler.next(e); }, onError: (e, handler) { print(e); return handler.next(e); }, ); List<Interceptor> inters = []; inters.add(CustomInterceptor()); //添加自定义拦截器 inters.add(LogInterceptor()); //添加内置拦截器 dio.interceptors.addAll(inters); // 3 发起网络请求 try { Response response = await dio.request<T>(url, queryParameters: params, options: options); print("测试"); print(url); return response.data; } catch (e) { print(e); return Future.error(e); } } }
调用请求
把封装好的文件导入进来,
import 'package:flutter/material.dart'; import 'utils/http.dart'; class MyNetwork extends StatefulWidget { const MyNetwork({super.key}); @override State<MyNetwork> createState() => _MyNetworkState(); } class _MyNetworkState extends State<MyNetwork> { void getData() async { print("发起请求"); var responseData = await HttpApi.request('book'); print(responseData); } @override Widget build(BuildContext context) { return Container( child: Column(children: [ ElevatedButton( onPressed: () { getData(); }, child: const Text("发起请求")) ])); } }
在 utils/http.dart
文件中,我们封装了 dio
,创建了dio
示例,传入了默认配置项,并且绑定了拦截器,在发起请求时,也会走请求拦截器,看参数是否合法,后会进入响应拦截器,状态码是否对以及不同状态码返回不同内容,最后将返回结果数据抛给客户端响应。
在utils/http.dart
文件中, 有一个 request
异步 静态(static)方法,它提供了请求时需要填的必填参数和可选参数,当我们使用时,可以通过 类名.方法名
即可调用发起请求了。
请求返回内容