Базовое использование инфраструктуры сетевых запросов флаттера dio

Flutter

Надпись—— Держа меч в мире, начиная со своего накопления, куда бы вы ни пошли, вы должны стремиться к совершенству, то есть подбрасывать каждый день.

важная новость


В этой статье будет описано 1. Используйте dio для отправки базового запроса на получение 2. Используйте dio для отправки метода параметра запроса на получение 3. Разберите ответные данные json 4. Используйте dio для отправки почтового запроса и отправки параметров FormData 5. Используйте dio для отправки почтового запроса и отправки параметров json. 6. Используйте dio для загрузки файлов и реализации мониторинга прогресса 7. Используйте dio для загрузки файлов и реализации мониторинга прогресса 8. Настраиваем перехватчик dio 9. Настройте сетевой прокси-сервер dio для захвата пакетов. 10. Настройте заголовок запроса dio и параметры публичного запроса. 11.dio отменяет сетевой запрос

1. Введение

dio — это фреймворк, используемый для доступа к сети в кроссплатформенной разработке флаттера, при его использовании мы сначала вводим зависимости

 dio: 3.0.9
2 Дио получить запрос
2.1 Dio получить запрос без параметров
  //get请求无参数
  void getRequestFunction1() async {
    ///创建Dio对象
    Dio dio = new Dio();
    ///请求地址 获取用户列表
    String url = "http://192.168.0.102:8080/getUserList";
    ///发起get请求
    Response response = await dio.get(url);
    ///响应数据
    var data = response.data;

    setState(() {
      result = data.toString();
    });
  }

результат ответа данных

{
    "code": 200,
    "data": [
        {
            "id": 3,
            "userName": "测试人员",
            "realName": "张三",
            "age": 22
        }
    ],
    "message": "请求成功"
}

Отладка точки останова выглядит следующим образом

在这里插入图片描述

2.2 Запрос Dio get имеет параметры
  ///get请求有参数
  ///根据用户ID来获取用户信息
  void getRequestFunction2() async {
    ///用户id
    int userId =3;
    ///创建 dio
    Dio dio = new Dio();
    ///请求地址
    ///传参方式1
    String url = "http://192.168.0.102:8080/getUser/$userId";
    ///传参方式2 
    String url2 = "http://192.168.0.102:8080/getUser?userId=$userId";
    ///传参方式 3
    String url3 = "http://192.168.0.102:8080/getUser";

    Map<String,dynamic> map = Map();
    map["userId"]= userId;
    ///发起get请求
    Response response = await dio.get(url3,queryParameters: map);

    ///响应数据
    Map<String,dynamic> data = response.data;
    /// 将响应数据解析为 UserBean
    UserBean userBean = UserBean.fromJson(data);
  }

}

В приведенном выше коде режим передачи параметров 1 и режим передачи параметров 2 представляют собой параметры сплайсинга в ссылке запроса, режим запроса 3 заключается в том, чтобы поместить параметры в карту, а затем настроить параметры с помощью параметров запроса Дио.. Структура данных, возвращенная выше, выглядит так:

{
    "code": 200,
    "data": {
        "id": 3,
        "userName": "测试人员",
        "realName": "张三",
        "age": 22
    },
    "message": "请求成功"
}

отладка точки останова

在这里插入图片描述
Для используемого здесь объекта модели данных UserBean


class UserBean{
  String userName;
  String realName;
  int age;
  int id;

  static UserBean fromJson(Map<String,dynamic> rootData){
    ///解析第一层
    Map<String,dynamic> data = rootData["data"];
    ///解析第二层
    UserBean userBean = new UserBean();

    userBean.id = data["id"];
    userBean.age = data["age"];
    userBean.userName= data["userName"];
    userBean.realName = data["realName"];
    return userBean;
    
  }
}

Для анализа данных в UserBean, как показано ниже

在这里插入图片描述

3 Почтовый запрос Дио
2.1 Почтовый запрос Dio для отправки данных формы FormData

FormData объединяет переданное имя параметра и значение для сериализации данных формы, тем самым уменьшая сращивание элементов формы. Его также можно описать следующим образом: интерфейс FormData предоставляет способ создания пар ключ-значение, представляющих данные формы.Тип кодирования запроса, отправляемого через FormData, устанавливается на «multipart/form-data», а в доступе к сетевому запросу — через Content -Type для записи этого значения, можно понять, что Content-Type представляет информацию о типе мультимедиа в конкретном запросе.

И Content-Type, который мы обычно используем в реальной разработке, выглядит следующим образом:

multipart/form-data
application/json     JSON数据格式
application/x-www-form-urlencoded 表单数据格式

Далее мы будем использовать dio для инициирования почтового запроса, а формат передаваемых параметров — FromData.

  void postRequestFunction() async {
    ///创建Dio
    Dio dio = new Dio();
    ///发送 FormData:
    FormData formData = FormData.fromMap({"name": "张三", "age": 22});
    String url ="http://192.168.200.68:8080/registerUser";
    ///发起 post 请求 如这里的注册用户信息
    Response response = await dio
        .post(url, data: formData);
    result = response.data.toString();
    setState(() {});
  }

Захват пакета выглядит следующим образом

在这里插入图片描述
Мы также можем увидеть формат параметра
在这里插入图片描述
Здесь мы видим, что Content-type имеет значение text/plain, а не multipart/form-data, как мы сказали выше, потому что при инкапсуляции параметров через FormData Dio будет выполняться настройка по умолчанию, как показано на рисунке ниже.

在这里插入图片描述

2.2 Почтовый запрос Dio для отправки данных json

Затем мы используем dio для инициирования почтового запроса и отправки параметров в формате json.

  ///post请求发送json
  void postRequestFunction2() async{
    String url = "http://192.168.0.102:8080/registerUser2";
    ///创建Dio
    Dio dio = new Dio();
    ///创建Map 封装参数
    Map<String,dynamic> map = Map();
    map['userName']="小明";
    map['userAge']=44;

    ///发起post请求
    Response response =  await dio.post(url,data: map);

    var data = response.data;
  }

Захват пакета выглядит следующим образом

在这里插入图片描述
На рисунке выше мы видим, что Content-Type указывает, что метод передачи параметра отправляется в формате json, На рисунке ниже мы видим конкретные параметры
在这里插入图片描述

4 Загрузка файлов Dio и мониторинг прогресса

  ///手机中的图片
  String localImagePath ="/storage/emulated/0/Download/17306285.jpg";
  ///上传的服务器地址
  String netUploadUrl = "http://192.168.0.102:8080/fileupload";

  ///dio 实现文件上传
  void fileUplod() async{
    ///创建Dio
    Dio dio = new Dio();

    Map<String ,dynamic> map = Map();
    map["auth"]="12345";
    map["file"] = await MultipartFile.fromFile(localImagePath,filename: "xxx23.png");
    ///通过FormData
    FormData formData = FormData.fromMap(map);
    ///发送post
    Response response = await dio.post(netUploadUrl, data: formData,
      ///这里是发送请求回调函数
      ///[progress] 当前的进度
      ///[total] 总进度
      onSendProgress: (int progress, int total) {
        print("当前进度是 $progress 总进度是 $total");
      },);
    ///服务器响应结果
    var data = response.data;

  }

Отладка с точками останова

在这里插入图片描述
Интерфейс запроса загрузки изображения здесь возвращает путь сохранения изображения, мы открываем каталог локального сервера
在这里插入图片描述

5 Загрузка файлов Dio и мониторинг прогресса

  ///当前进度进度百分比  当前进度/总进度 从0-1
  double currentProgress =0.0;
  ///下载文件的网络路径
  String apkUrl ="";
  ///使用dio 下载文件
  void downApkFunction() async{
    /// 申请写文件权限
    bool isPermiss =  await checkPermissFunction();
    if(isPermiss) {
      ///手机储存目录
      String savePath = await getPhoneLocalPath();
      String appName = "rk.apk";

      ///创建DIO
      Dio dio = new Dio();

      ///参数一 文件的网络储存URL
      ///参数二 下载的本地目录文件
      ///参数三 下载监听
      Response response = await dio.download(
          apkUrl, "$savePath$appName", onReceiveProgress: (received, total) {
        if (total != -1) {
          ///当前下载的百分比例
          print((received / total * 100).toStringAsFixed(0) + "%");
          // CircularProgressIndicator(value: currentProgress,) 进度 0-1
          currentProgress = received / total;
          setState(() {

          });
        }
      });
    }else{
      ///提示用户请同意权限申请
    }
  }

Разрешения Android в настоящее время делятся на три типа: обычные разрешения, опасные разрешения и специальные разрешения.

Обычные разрешения Разрешения, которые можно получить, настроив непосредственно в файле AndroidManifest. Большинство разрешений находятся здесь. Опасные разрешения, некоторые разрешения определены здесь после Android 6.0. Опасные разрешения нужно не только настроить в AndroidManifest, но и проверить, действительно ли у них есть разрешения, прежде чем использовать их для динамического применения к ним.

В ios используйте xcode, чтобы открыть этот каталог

Выберите файл info.plist в проекте Xcode, щелкните правой кнопкой мыши и выберите «Открыть как — исходный код» и скопируйте в него код конфигурации разрешений.Содержимое пары «ключ-значение» можно изменить в соответствии с требованиями проекта.

<!-- 相册 --> 
<key>NSPhotoLibraryUsageDescription</key> 
<string>需要您的同意,APP才能访问相册</string> 
<!-- 相机 --> 
<key>NSCameraUsageDescription</key> 
<string>需要您的同意,APP才能访问相机</string> 
<!-- 麦克风 --> 
<key>NSMicrophoneUsageDescription</key> 
<string>需要您的同意,APP才能访问麦克风</string> 
<!-- 位置 --> 
<key>NSLocationUsageDescription</key> 
<string>需要您的同意, APP才能访问位置</string> 
<!-- 在使用期间访问位置 --> 
<key>NSLocationWhenInUseUsageDescription</key> 
<string>App需要您的同意, APP才能在使用期间访问位置</string> 
<!-- 始终访问位置 --> 
<key>NSLocationAlwaysUsageDescription</key> 
<string>App需要您的同意, APP才能始终访问位置</string> 
<!-- 日历 --> 
<key>NSCalendarsUsageDescription</key> 
<string>App需要您的同意, APP才能访问日历</string> 
<!-- 提醒事项 --> 
<key>NSRemindersUsageDescription</key> 
<string>需要您的同意, APP才能访问提醒事项</string> 
<!-- 运动与健身 --> 
<key>NSMotionUsageDescription</key> 
<string>需要您的同意, APP才能访问运动与健身</string> 
<!-- 健康更新 --> 
<key>NSHealthUpdateUsageDescription</key> 
<string>需要您的同意, APP才能访问健康更新 </string> 
<!-- 健康分享 --> 
<key>NSHealthShareUsageDescription</key> 
<string>需要您的同意, APP才能访问健康分享</string> 
<!-- 蓝牙 --> 
<key>NSBluetoothPeripheralUsageDescription</key> 
<string>需要您的同意, APP才能访问蓝牙</string> 
<!-- 媒体资料库 --> 
<key>NSAppleMusicUsageDescription</key> 
<string>需要您的同意, APP才能访问媒体资料库</string>

В каталоге проекта флаттера мы также можем открыть конфигурацию файла info.plist, как показано ниже.

在这里插入图片描述
Плагин разрешения_обработчика используется здесь для подачи заявок на разрешения

permission_handler: ^4.3.0

Запрос кода доступа выглядит следующим образом

  ///PermissionGroup.storage 对应的是 
  ///android 的外部存储 (External Storage)
  ///ios 的Documents` or `Downloads`
  checkPermissFunction() async {
    if (Theme.of(context).platform == TargetPlatform.android) {
      ///安卓平台中 checkPermissionStatus方法校验是否有储存卡的读写权限 
      PermissionStatus permission = await PermissionHandler()
          .checkPermissionStatus(PermissionGroup.storage);
      if (permission != PermissionStatus.granted) {
        ///无权限那么 调用方法 requestPermissions 申请权限
        Map<PermissionGroup, PermissionStatus> permissions =
            await PermissionHandler()
                .requestPermissions([PermissionGroup.storage]);
        ///校验用户对权限申请的处理
        if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
          return true;
        }
      } else {
        return true;
      }
    } else {
      return true;
    }
    return false;
  }

После подачи заявки на разрешение нам нужно определить путь к карте памяти, здесь мы используем плагин path_provider.

path_provider: 1.6.0
  ///获取手机的存储目录路径
  ///getExternalStorageDirectory() 获取的是  android 的外部存储 (External Storage)
  ///  getApplicationDocumentsDirectory 获取的是 ios 的Documents` or `Downloads` 目录
  Future<String> getPhoneLocalPath() async {
    final directory = Theme.of(context).platform == TargetPlatform.android
        ? await getExternalStorageDirectory()
        : await getApplicationDocumentsDirectory();
    return directory.path;
  }
6 dio настраивает сетевой прокси для захвата пакетов
  _setupPROXY(Dio dio) {
    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
        (HttpClient client) {
      client.findProxy = (uri) {
        ///这里的 192.168.0.102:8888就是我们的代理服务地址
        return "PROXY 192.168.0.102:8888";
      };
      client.badCertificateCallback =
          (X509Certificate cert, String host, int port) {
        return true;
      };
    };
  }
7 dio настраивает параметры публичного запроса

В реальной разработке приложения у нас будут параметры, которые необходимо передавать для каждого запроса интерфейса, такие как токен, appVersionCode и т. д., которые называются параметрами публичного запроса, тогда мы можем рассмотреть эту конфигурацию в запросе dio здесь

  String application = "V 1.2.2";
  int appVersionCode = 122;
  ///[url]网络请求链接 
  ///[data] post 请求时传的json数据
  ///[queryParameters] get请求时传的参数
  void configCommonPar(url,data,Map<String, dynamic> queryParameters){
    ///配制统一参数
    if (data != null) {
      data['application'] = application;
      data['appVersionCode'] = appVersionCode.toString();
    } else if (queryParameters != null) {
      queryParameters['application'] = application;
      queryParameters['appVersionCode'] = appVersionCode.toString();
    } else {
      ///url中有可能拼接着其他参数
      if (url.contains("?")) {
        url += "&application=$application&appVersionCode=$appVersionCode";
      } else {
        url += "?application=$application&appVersionCode=$appVersionCode";
      }
    }
  }
}

8 dio настраивает Content-Type и заголовок запроса

Когда мы создаем объект Dio, мы инициализируем BaseOptions для создания Dio.

      BaseOptions options = BaseOptions();
      ///请求header的配置
      options.headers["appVersionCode"]=406;
      options.headers["appVersionName"]="V 4.0.6";
      
      options.contentType="application/json";
      options.method="GET";
      options.connectTimeout=30000;
      ///创建 dio
      Dio dio = new Dio(options);

Мы также можем получить параметры по умолчанию через dio и изменять их каждый раз, когда отправляем разные запросы, такие как get и post.

void getRequestFunction2() async {
    ///用户id
    int userId = 3;
    ///创建 dio
    Dio dio = new Dio();

    ///请求地址
    ///传参方式1
    String url = "http://192.168.0.102:8080/getUser/$userId";
    ///在这里修改 contentType
    dio.options.contentType="application/json";
    ///请求header的配置
    dio.options.headers["appVersionCode"]=406;
    dio.options.headers["appVersionName"]="V 4.0.6";
    ///发起get请求
    Response response = await dio.get(url);

  ...
  }
9 dio отменяет сетевой запрос

В реальной разработке, например, когда мы выходим со страницы, если сетевой запрос не выполнен, это вызовет утечку памяти, поэтому необходимо отменить сетевой запрос, когда страница будет уничтожена, или при загрузке файла, время слишком велико, и пользователь нажимает кнопку «Отмена», чтобы отменить сетевое подключение.

///创建取消标志
CancelToken cancelToken = new CancelToken();
void getRequestFunction2() async {
    ///用户id
    int userId = 3;
    ///创建 dio
    Dio dio = new Dio();
    ///请求地址
    ///传参方式1
    String url = "http://192.168.0.102:8080/getUser/$userId";
    ///发起get请求 并设置 CancelToken 取消标志
    Response response = await dio.get(url,cancelToken: cancelToken);

  ...
  }

Затем, когда нам нужно вручную отменить этот сетевой запрос, нам нужно только вызвать следующий метод

   ///取消网络请求
   if(cancelToken!=null&&!cancelToken.isCancelled){
      cancelToken.cancel();
      cancelToken=null;
    }

Следует отметить, что CancelToken можно использовать только для одного сетевого запроса.


полный