赞
踩
https://github.com/cfug/dio/blob/main/dio/README-ZH.md
手动添加到pubspec.yaml:
- dependencies:
- dio: ^替换为最新版本
在终端使用以下命令:
$ dart pub add dio
dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。
单例模式详见:Flutter之单例模式的四种方法_YUFENGSHI.LJ的博客-CSDN博客
- class HttpManager{
-
- //1、通过静态方法 getInstance() 访问实例—————— getInstance() 构造、获取、返回实例
- /*通过工厂方法获取该类的实例,将实例对象按对应的方法返回出去
- *实例不存在时,调用命名构造方法获取一个新的实例 */
- static HttpManager getInstance(){
- if(_instance==null){
- _instance=HttpManager._internal();
- }
- return _instance!;
- }
-
- //2、静态属性——该类的实例
- static HttpManager? _instance=HttpManager._internal();
-
- //3、私有的命名构造函数,确保外部不能拿到它————初始化实例
- HttpManager._internal(){}
-
- //4.1、创建一个 Dio 实例
- late Dio dio;
可以使用默认配置或传递一个可选 BaseOptions
参数来创建一个Dio实例
BaseOptions
描述的是 Dio 实例发起网络请求的的公共配置,而 Options
描述了每一个Http请求的配置信息,每一次请求都可以单独配置。
单次请求的 Options
中的配置信息可以覆盖 BaseOptions
中的配置。
BaseOptions
:基类请求配置
- //请求方式
- String? method,
-
- //连接超时时间
- Duration? connectTimeout,
-
- //接收超时
- Duration? receiveTimeout,
-
- //发送超时
- Duration? sendTimeout,
-
- //基本网址
- String baseUrl = '',
-
- //请求包头
- Map<String, dynamic>? headers,
-
- //以何种方式接收响应数据,默认是json
- ResponseType? responseType = ResponseType.json,
-
- //内容类型
- String? contentType,
-
- Map<String, dynamic>? queryParameters,
-
- Map<String, dynamic>? extra,
-
- ValidateStatus? validateStatus,
-
- bool? receiveDataWhenStatusError,
-
- bool? followRedirects,
-
- int? maxRedirects,
-
- bool? persistentConnection,
-
- RequestEncoder? requestEncoder,
-
- ResponseDecoder? responseDecoder,
-
- ListFormat? listFormat,
Options
: 单次请求配置
- /// 请求方式。
- String method;
-
- /// 请求基本地址,可以包含路径例如 https://dart.dev/api/。
- String? baseUrl;
-
- /// HTTP 请求头。
- Map<String, dynamic>? headers;
-
- /// 连接服务器超时时间.
- Duration? connectTimeout;
-
- /// 两次数据流数据接收的最长间隔时间,注意不是请求的最长接收时间。
- Duration? receiveTimeout;
-
- /// 请求内容体,可以是任意类型。
- dynamic data;
-
- /// 请求路径,如果以 http(s)开始, 则 [baseURL] 会被忽略,
- /// 否则将会和 [baseUrl] 拼接出完整的地址。
- String path = '';
-
- /// 请求的 Content-Type。
- ///
- /// 默认值会由 [ImplyContentTypeInterceptor] 根据请求载荷类型进行推导。
- /// 可以调用 [Interceptors.removeImplyContentTypeInterceptor] 进行移除。
- ///
- /// 如果你想以 `application/x-www-form-urlencoded` 格式编码请求数据,
- /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,
- /// [Dio] 会自动编码请求体。
- String? contentType;
-
- /// 期望以哪种格式(方式)接受响应数据,包括 `json`、`stream` 和 `plain`。
- ///
- /// 默认值是 `json`, 当响应头中 content-type 为 `application/json` 时,
- /// dio 会自动将响应内容转化为 json 对象。
- /// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `stream`。
- ///
- /// 如果想以文本(字符串)格式接收响应数据,请使用 `plain`。
- ResponseType? responseType;
-
- /// `validateStatus` 决定 HTTP 响应状态码是否被视为请求成功,
- /// 返回 `true` 请求结果就会按成功处理,否则会按失败处理.
- ValidateStatus? validateStatus;
-
- /// 用户自定义字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中依次传递。
- Map<String, dynamic>? extra;
-
- /// 请求地址的参数。
- Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters;
-
- /// 请求数据中数组的编码的方式,默认值为 `multiCompatible`。
- ListFormat? listFormat;
实际请求配置,即[BaseOptions]和[Options]组合后的最终结果。
- /// The internal request option class that is the eventual result after
- /// [BaseOptions] and [Options] are composed.
- class RequestOptions extends _RequestConfig with OptionsMixin {
- RequestOptions({
- this.path = '',
- this.data,
- this.onReceiveProgress,
- this.onSendProgress,
- this.cancelToken,
- String? method,
- Duration? sendTimeout,
- Duration? receiveTimeout,
- Duration? connectTimeout,
- Map<String, dynamic>? queryParameters,
- String? baseUrl,
- Map<String, dynamic>? extra,
- Map<String, dynamic>? headers,
- ResponseType? responseType,
- String? contentType,
- ValidateStatus? validateStatus,
- bool? receiveDataWhenStatusError,
- bool? followRedirects,
- int? maxRedirects,
- bool? persistentConnection,
- RequestEncoder? requestEncoder,
- ResponseDecoder? responseDecoder,
- ListFormat? listFormat,
- bool? setRequestContentTypeWhenNoPayload,
- StackTrace? sourceStackTrace,
- }) : assert(connectTimeout == null || !connectTimeout.isNegative),
- super(
- method: method,
- sendTimeout: sendTimeout,
- receiveTimeout: receiveTimeout,
- extra: extra,
- headers: headers,
- responseType: responseType,
- contentType: contentType,
- validateStatus: validateStatus,
- receiveDataWhenStatusError: receiveDataWhenStatusError,
- followRedirects: followRedirects,
- maxRedirects: maxRedirects,
- persistentConnection: persistentConnection,
- requestEncoder: requestEncoder,
- responseDecoder: responseDecoder,
- listFormat: listFormat,
- ) {
- this.sourceStackTrace = sourceStackTrace ?? StackTrace.current;
- this.queryParameters = queryParameters ?? {};
- this.baseUrl = baseUrl ?? '';
- this.connectTimeout = connectTimeout;
- }
-
- /// Create a [RequestOptions] from current instance with merged attributes.
- RequestOptions copyWith({
- String? method,
- Duration? sendTimeout,
- Duration? receiveTimeout,
- Duration? connectTimeout,
- dynamic data,
- String? path,
- Map<String, dynamic>? queryParameters,
- String? baseUrl,
- ProgressCallback? onReceiveProgress,
- ProgressCallback? onSendProgress,
- CancelToken? cancelToken,
- Map<String, dynamic>? extra,
- Map<String, dynamic>? headers,
- ResponseType? responseType,
- String? contentType,
- ValidateStatus? validateStatus,
- bool? receiveDataWhenStatusError,
- bool? followRedirects,
- int? maxRedirects,
- bool? persistentConnection,
- RequestEncoder? requestEncoder,
- ResponseDecoder? responseDecoder,
- ListFormat? listFormat,
- bool? setRequestContentTypeWhenNoPayload,
- }) {
- final contentTypeInHeader = headers != null &&
- headers.keys
- .map((e) => e.toLowerCase())
- .contains(Headers.contentTypeHeader);
-
- assert(
- !(contentType != null && contentTypeInHeader),
- 'You cannot set both contentType param and a content-type header',
- );
-
- final ro = RequestOptions(
- method: method ?? this.method,
- sendTimeout: sendTimeout ?? this.sendTimeout,
- receiveTimeout: receiveTimeout ?? this.receiveTimeout,
- connectTimeout: connectTimeout ?? this.connectTimeout,
- data: data ?? this.data,
- path: path ?? this.path,
- baseUrl: baseUrl ?? this.baseUrl,
- queryParameters: queryParameters ?? Map.from(this.queryParameters),
- onReceiveProgress: onReceiveProgress ?? this.onReceiveProgress,
- onSendProgress: onSendProgress ?? this.onSendProgress,
- cancelToken: cancelToken ?? this.cancelToken,
- extra: extra ?? Map.from(this.extra),
- headers: headers ?? Map.from(this.headers),
- responseType: responseType ?? this.responseType,
- validateStatus: validateStatus ?? this.validateStatus,
- receiveDataWhenStatusError:
- receiveDataWhenStatusError ?? this.receiveDataWhenStatusError,
- followRedirects: followRedirects ?? this.followRedirects,
- maxRedirects: maxRedirects ?? this.maxRedirects,
- persistentConnection: persistentConnection ?? this.persistentConnection,
- requestEncoder: requestEncoder ?? this.requestEncoder,
- responseDecoder: responseDecoder ?? this.responseDecoder,
- listFormat: listFormat ?? this.listFormat,
- sourceStackTrace: sourceStackTrace,
- );
-
- if (contentType != null) {
- ro.headers.remove(Headers.contentTypeHeader);
- ro.contentType = contentType;
- } else if (!contentTypeInHeader) {
- ro.contentType = this.contentType;
- }
-
- return ro;
- }
-
- /// The source [StackTrace] which should always point to the invocation of
- /// [DioMixin.request] or if not provided, to the construction of the
- /// [RequestOptions] instance. In both instances the source context should
- /// still be available before it is lost due to asynchronous operations.
- @internal
- StackTrace? sourceStackTrace;
-
- /// Generate the requesting [Uri] from the options.
- Uri get uri {
- String url = path;
- if (!url.startsWith(RegExp(r'https?:'))) {
- url = baseUrl + url;
- final s = url.split(':/');
- if (s.length == 2) {
- url = '${s[0]}:/${s[1].replaceAll('//', '/')}';
- }
- }
- final query = Transformer.urlEncodeQueryMap(queryParameters, listFormat);
- if (query.isNotEmpty) {
- url += (url.contains('?') ? '&' : '?') + query;
- }
- // Normalize the url.
- return Uri.parse(url).normalizePath();
- }
-
- /// Request data in dynamic types.
- dynamic data;
-
- /// Defines the path of the request. If it starts with "http(s)",
- /// [baseUrl] will be ignored. Otherwise, it will be combined and resolved
- /// with the [baseUrl].
- String path;
-
- /// {@macro dio.CancelToken}
- CancelToken? cancelToken;
-
- /// {@macro dio.options.ProgressCallback}
- ProgressCallback? onReceiveProgress;
-
- /// {@macro dio.options.ProgressCallback}
- ProgressCallback? onSendProgress;
- }
- /*在私有构造方法中,在里面可以进行初始化dio实例*/
- HttpManager._internal(){
- //4.2、设置BaseOptions
- BaseOptions baseOptions=BaseOptions(
- //基本网址
- baseUrl:"https://lionstock-uat-new.chinaeast2.cloudapp.chinacloudapi.cn:8200/",
- //连接超时
- connectTimeout: Duration(milliseconds: 30000),
- //接收超时
- receiveTimeout: Duration(milliseconds: 5000),
- //包头
- headers: {
- "Content-Type": "application/json;Charset=UTF-8",
- "connect":"get"
- },
- //内容类型
- contentType: 'application/json;Charset=UTF-8',
- //响应类型——期待已那种方式接收数据,默认是json
- responseType: ResponseType.json,
- );
- //4.3 初始化dio实例
- dio=new Dio(baseOptions) ;
- //添加一个拦截器
- dio.interceptors.add(new DioLogInterceptor());
- }
当请求成功时会返回一个Response对象,它包含如下字段:
- /// 响应数据。可能已经被转换了类型, 详情请参考 [ResponseType]。
- T? data;
-
- /// 响应对应的实际请求配置。
- RequestOptions requestOptions;
-
- /// 响应的 HTTP 状态码。
- int? statusCode;
-
- /// 响应对应状态码的详情信息。
- String? statusMessage;
-
- /// 响应是否被重定向
- bool isRedirect;
-
- /// 请求连接经过的重定向列表。如果请求未经过重定向,则列表为空。
- List<RedirectRecord> redirects;
-
- /// 在 [RequestOptions] 中构造的自定义字段。
- Map<String, dynamic> extra;
-
- /// 响应对应的头数据(响应头)
- Headers headers;
get方法中只有路径是必填的,
- Future<Response<T>> get<T>(
- String path, {
- Object? data,
- Map<String, dynamic>? queryParameters,
- Options? options,
- CancelToken? cancelToken,
- ProgressCallback? onReceiveProgress,
- });
get
方法使用指定的路径和查询参数向服务器发送 HTTP GET 请求。它还允许您传递其他选项,例如标头、响应类型和超时。该方法返回一个 Future 对象,该对象使用包含来自服务器的 HTTP 响应的 Response 对象进行解析。
- get(String url,{option,params}) async {
- Response response;
- try{
- response=await dio.get(url,options: Options(responseType: ResponseType.json));
- print("response.data:${response.data}");
- print("response.data:${response.statusCode}");
- print("response.data:${response.statusMessage}");
- print("response.data:${response.headers}");
-
- }
- on Exception catch(e){
- print("Get方法出错:${e.toString()}");
- }
-
- }
接口:https://api.github.com/orgs/flutterchina/repos
- HttpManager.getInstance().get(
- "https://reqres.in/api/users",
- option: Options(responseType: ResponseType.plain),
- );
- // 获取id 法一
- HttpManager.getInstance().get("https://reqres.in/api/users/1");
-
- // 获取id 法二
- HttpManager.getInstance().get("https://reqres.in/api/users?id=2");
- // 获取id 法三 用参数类型
- Map<String,dynamic> map = Map();
- map["id"]= 3;
- HttpManager.getInstance().get(
- "https://reqres.in/api/users",
- option: Options(responseType: ResponseType.json),
- params:map
- );
在发出 GET 请求时,我们通常不传递任何数据。但是当发出 POST、PUT、DELETE 等请求时,我们需要传递正文/数据。
post
方法与方法类似get
,但增加了一个data
参数,该参数代表请求正文。使用该方法获取请求标头getAuthorizationHeader
并与提供的任何选项合并。使用发出请求dio.post
,如果成功则返回响应数据。如果请求由于 Dio 错误而失败,ErrorEntity
则会抛出异常。
- post(api,{params}) async {
- Response response;
- //请求参数 为空时,配置
- if(params==null){
- params["marketNo"] = "PC_Flutter";
- params["versionNo"] = '10105';/*版本号*/
- params["token"] = '6b2fc908787c428ab16559fce9d86bf2';
- params["uid"] = '201323';
- }
-
- try{
- response=await dio.post(
- api,
- queryParameters: params,
- );
- print("post response:${response.data}\n");
- }
- on Exception catch (e){
- print("post出错:${e.toString()}");
- }
-
- }
- HttpManager.getInstance().post(
- "https://www.wanandroid.com/user/register",
- params: {
- "username": "zsdhwiehfwo",
- "password": "123456",
- "repassword": "123456"}
- );
post response:{data: {admin: false, chapterTops: [], coinCount: 0, collectIds: [], email: , icon: , id: 151550, nickname: zsdhwiehfwo, password: , publicName: zsdhwiehfwo, token: , type: 0, username: zsdhwiehfwo}, errorCode: 0, errorMsg: }
每个 Dio 实例都可以添加任意多个拦截器,他们会组成一个队列,拦截器队列的执行顺序是先进先出。 通过使用拦截器,你可以在请求之前、响应之后和发生异常时(未被 then
或 catchError
处理) 做一些统一的预处理操作。
- /// Deliver the [response] to the next interceptor.
- ///
- /// Typically, the method should be called once interceptors done
- /// manipulating the [response].
- ///将[响应]传递给下一个拦截器。通常,一旦拦截器完成操作[响应],就应该调用该方法。
- void next(Response response) {
- _completer.complete(
- InterceptorState<Response>(response),
- );
- _processNextInQueue?.call();
- }
如果不调用handler.next(response)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。
- /// Called when the request is about to be sent.
- void onRequest(
- RequestOptions options,
- RequestInterceptorHandler handler,
- ) {
- handler.next(options);
- }
RequestOptions options,表示本次请求的实际配置
RequestInterceptorHandler是一个拦截器处理器,用于处理请求拦截器中的逻辑。
- /// The handler for interceptors to handle after respond.
- class ResponseInterceptorHandler extends _BaseHandler {
- /// Deliver the [response] to the next interceptor.
- ///
- /// Typically, the method should be called once interceptors done
- /// manipulating the [response].
- void next(Response response) {
- _completer.complete(
- InterceptorState<Response>(response),
- );
- _processNextInQueue?.call();
- }
-
- /// Completes the request by resolves the [response] as the result.
- void resolve(Response response) {
- _completer.complete(
- InterceptorState<Response>(
- response,
- InterceptorResultType.resolve,
- ),
- );
- _processNextInQueue?.call();
- }
-
- /// Completes the request by reject with the [error] as the result.
- ///
- /// Invoking the method will make the rest of interceptors in the queue
- /// skipped to handle the request,
- /// unless [callFollowingErrorInterceptor] is true
- /// which delivers [InterceptorResultType.rejectCallFollowing]
- /// to the [InterceptorState].
- void reject(DioException error,
- [bool callFollowingErrorInterceptor = false]) {
- _completer.completeError(
- InterceptorState<DioException>(
- error,
- callFollowingErrorInterceptor
- ? InterceptorResultType.rejectCallFollowing
- : InterceptorResultType.reject,
- ),
- error.stackTrace,
- );
- _processNextInQueue?.call();
- }
- }
- /// Called when the response is about to be resolved.
- ///当响应即将解决时调用。
- void onResponse(
- Response response,
- ResponseInterceptorHandler handler,
- ) {
- handler.next(response);
- }
response表示响应数据,包括响应状态码、响应头、响应数据等
ResponseInterceptorHandler是一个拦截器处理器,用于处理响应拦截器中的逻辑。
- /// The handler for interceptors to handle after respond.
- class ResponseInterceptorHandler extends _BaseHandler {
- /// Deliver the [response] to the next interceptor.
- ///
- /// Typically, the method should be called once interceptors done
- /// manipulating the [response].
- void next(Response response) {
- _completer.complete(
- InterceptorState<Response>(response),
- );
- _processNextInQueue?.call();
- }
-
- /// Completes the request by resolves the [response] as the result.
- void resolve(Response response) {
- _completer.complete(
- InterceptorState<Response>(
- response,
- InterceptorResultType.resolve,
- ),
- );
- _processNextInQueue?.call();
- }
-
- /// Completes the request by reject with the [error] as the result.
- ///
- /// Invoking the method will make the rest of interceptors in the queue
- /// skipped to handle the request,
- /// unless [callFollowingErrorInterceptor] is true
- /// which delivers [InterceptorResultType.rejectCallFollowing]
- /// to the [InterceptorState].
- void reject(DioException error,
- [bool callFollowingErrorInterceptor = false]) {
- _completer.completeError(
- InterceptorState<DioException>(
- error,
- callFollowingErrorInterceptor
- ? InterceptorResultType.rejectCallFollowing
- : InterceptorResultType.reject,
- ),
- error.stackTrace,
- );
- _processNextInQueue?.call();
- }
- }
- /// Called when an exception was occurred during the request.
- ///当请求过程中发生异常时调用。
- void onError(
- DioException err,
- ErrorInterceptorHandler handler,
- ) {
- handler.next(err);
- }
- class DioLogInterceptor extends Interceptor{
-
- ///请求前
- @override
- Future onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
- String requestStr = "\n==================== 请求前拦截——REQUEST ====================\n"
- "- URL:\n${options.baseUrl + options.path}\n"
- "- METHOD: ${options.method}\n";
-
-
- requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";
-
- final data = options.data;
- if (data != null) {
- if (data is Map)
- requestStr += "- BODY:\n${data.mapToStructureString()}\n";
- else if (data is FormData) {
- final formDataMap = Map()..addEntries(data.fields)..addEntries(data.files);
- requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";
- } else
- requestStr += "- BODY:\n${data.toString()}\n";
- }
- print(requestStr);
- return handler.next(options);
- }
-
- }
- //添加一个拦截器
- dio.interceptors.add(new DioLogInterceptor());
当请求过程中发生错误时, Dio 会将 Error/Exception
包装成一个 DioException
:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。