Flutter 网络请求框架封装

作者: 8e750c8f0fae | 来源:发表于2019-03-30 19:57 被阅读17次

    Flutter 请求网络的三种方式

    flutter 请求网络的方式有三种,分别是 Dart 原生的网络请求 HttpClient、第三方网络请求 http以及 Flutter 中的 Dio。我们可以比较一下这三种网络请求方式,然后封装为我们方便请求网络的工具类。

    Dart 原生的网络请求 HttpClient

    实现 Dart 获取网络数据的请求,一般我们需要以下几个步骤:

    • step 1:
      原生的网络请求时不需要修改 pubspec.yaml 文件的,我们只需要在使用的地方引入所需包就可以了
    import 'dart:convert';
    import 'dart:io';
    
    • step 2:创建一个HttpClient
    HttpClient httpClient = new HttpClient();
    
    • step 3: 打开Http连接,设置请求头
    HttpClientRequest request = await httpClient.getUrl(uri);
    

    在这一步中,我们可以设置人意的的请求方法,比如 Get 请求、Post 请求、Delete 请求。

    例如:携带参数的请求

    Uri uri=Uri(scheme: "https", host: "flutterchina.club", queryParameters: {
        "userName":"chen",
        "password":"123456"
      });
    

    例如:设置请求的 header

    request.headers.add("user-agent", "test");
    request.headers.add("Authorization", "LKSJDLFJSDLKJSLKklsdj");
    
    • step 4: 等待连接服务器
    HttpClientResponse response = await request.close();
    
    • step 5: 读取响应内容
    if (response.statusCode == HttpStatus.ok) {
          _content = await response.transform(Utf8Decoder()).join();
    }
    
    • step 6: 断开连接
    httpClient.close();
    

    以上的步骤是 dart 简单获取网络的方式,我们从上面可以看到,通过 HttpClient 发起网络请求时比较麻烦的,很多都要我们亲手处理,还有 Cookie 的管理也是比较麻烦的。

    库 http

    • step 1:pubspec.yaml 添加依赖
    http: '>=0.11.3+12'
    
    • step 2: 在使用的地方导包
    import 'package:http/http.dart' as http;
    
    • step 3: 发起请求


    Get 请求

    void getRequest() async {
        var client = http.Client();
        http.Response response = await client.get(url_2);
        _content = response.body;
      }
    


    Post 请求

      void postRequest() async {
        var params = Map<String, String>();
        params["username"] = "hellonews";
        params["password"] = "123456";
    
        var client = http.Client();
        var response = await client.post(url_post, body: params);
        _content = response.body;
      }
    

    相对比 Dart 原生的网络请求,第三方库 http 的网络请求方式是要方便好多,写起来也是挺爽的。

    Flutter 发布的 dio

    Dio 一个强大的 Dart Http 请求库,支持 Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时等...

    • step 1:pubspec.yaml 添加依赖
    dependencies:
      dio: ^1.0.9
    
    • step 2:导入引用包
    import 'package:dio/dio.dart';
    
    • step 3:发起网络请求


    Get 请求

    void getRequest() async {
        Dio dio = new Dio();
        var response = await dio.get("/test?id=12&name=chen");
        _content = response.data.toString();
      }
    

    对于 query 参数,我们可以通过对象来进行传递,上面的代码等同于:

    void getRequest() async {
        Dio dio = new Dio();
        var response = await dio.get("/test",data:{"id":12,"name":"chen"});
        _content = response.data.toString();
      }
    

    Post 请求

      void postRequest() async {
        var dio = new Dio();
        var response = await dio.post(url_post, data:{"id":12,"name":"wendu"});
        _content = response.data.toString();
      }
    

    Dio 网络请求框架封装

    日志信息拦截

    Dio 和 okhttp 一样,都会有一个请求拦截器和响应拦截器,通过拦截器,我们可以在请求之前或响应之后做一些同意的预处理。例如我们发起请求前查看我们请求的参数和头部,响应的时候,我们可以查看返回来的数据。

        Dio dio = new Dio();
        // 添加拦截器
        if (Config.DEBUG) {
          dio.interceptors.add(InterceptorsWrapper(
              onRequest: (RequestOptions options){
                print("\n================== 请求数据 ==========================");
                print("url = ${options.uri.toString()}");
                print("headers = ${options.headers}");
                print("params = ${options.data}");
              },
              onResponse: (Response response){
                print("\n================== 响应数据 ==========================");
                print("code = ${response.statusCode}");
                print("data = ${response.data}");
                print("\n");
              },
              onError: (DioError e){
                print("\n================== 错误响应数据 ======================");
                print("type = ${e.type}");
                print("message = ${e.message}");
                print("stackTrace = ${e.stackTrace}");
                print("\n");
              }
          ));
        }
    

    如果我们想要移除拦截器,那么我们可以将其设置为 null

    dio.interceptor.request.onSend=null;
    dio.interceptor.response.onSuccess=null;
    dio.interceptor.response.onError=null;
    

    token 添加

        //  头部添加 token 验证
        headers["Authorization"] = "token lskjdlklsjkdklsjd333";
        option.headers = headers;
        ///超时
        option.connectTimeout = 15000;
        try {
          Response response = await dio.request(url, data: params, options: option);
        } on DioError catch (e) {
          // 请求错误处理
        }
    

    自动生成 dart 的 json 实体类插件 FlutterJsonBeanFactory

    在 Android 开发中,有 GsonFormat 这个插件来讲 json 数据自动转化成 Bean;那么在 Flutter 中也有类似的插件可以生产序列化的实体类的插件:FlutterJsonBeanFactory

    • step 1:下载插件 FlutterJsonBeanFactory,安装完成后重启
    Setting -> Plugins -> Browse Respositories 中搜索 FlutterJsonBeanFactory
    
    • step 2:创建实体类,在指定目录下:
    New -> dart bean class File from JSON
    
    jsontobean.png
    • step 3:输入实体类名及 json 格式的数据


      format.png
    • step 4:最后生成的实体类:LoginEntity

    class LoginEntity {
        String easemobpassword;
        String username;
    
        LoginEntity({this.easemobpassword, this.username});
    
        LoginEntity.fromJson(Map<String, dynamic> json) {
            easemobpassword = json['easemobPassword'];
            username = json['username'];
        }
    
        Map<String, dynamic> toJson() {
            final Map<String, dynamic> data = new Map<String, dynamic>();
            data['easemobPassword'] = this.easemobpassword;
            data['username'] = this.username;
            return data;
        }
    }
    

    请求错误处理

        Response response;
        try {
          response = await dio.request(url, data: params, options: option);
        } on DioError catch (e) {
          // 请求错误处理
          Response errorResponse;
          if (e.response != null) {
            errorResponse = e.response;
          } else {
            errorResponse = new Response(statusCode: 666);
          }
          if (e.type == DioErrorType.CONNECT_TIMEOUT) {
            errorResponse.statusCode = Code.NETWORK_TIMEOUT;
          }
          if (Config.DEBUG) {
            print('请求异常: ' + e.toString());
            print('请求异常 url: ' + url);
          }
          return new ResultData(Code.errorHandleFunction(errorResponse.statusCode, e.message, noTip), false, errorResponse.statusCode);
        }
    

    其中 ResultData 是网络结果处理的实体类

    /**
     * 网络结果数据
     * Created by chenjianrun
     * Date: 2018-07-16
     */
    class ResultData {
      var data;
      bool result;
      int code;
      var headers;
    
      ResultData(this.data, this.result, this.code, {this.headers});
    }
    
    

    Code 是处理网络错误的编码,并将错误结果通过 eventbus 发送出去,一般我们可以在 main_pager 中注册监听这个事件。

    ///网络请求错误编码
    class Code {
      ///网络错误
      static const NETWORK_ERROR = -1;
    
      ///网络超时
      static const NETWORK_TIMEOUT = -2;
    
      ///网络返回数据格式化一次
      static const NETWORK_JSON_EXCEPTION = -3;
    
      static const SUCCESS = 200;
    
      static final EventBus eventBus = new EventBus();
    
      static errorHandleFunction(code, message, noTip) {
        if(noTip) {
          return message;
        }
        eventBus.fire(new HttpErrorEvent(code, message));
        return message;
      }
    }
    

    完成的网络请求类:HttpRequest

    import 'dart:io';
    
    import 'package:dio/dio.dart';
    import 'package:private_tutor/common/SpUtils.dart';
    import 'package:connectivity/connectivity.dart';
    
    import 'dart:collection';
    
    import 'package:private_tutor/common/config/Config.dart';
    import 'package:private_tutor/net/ResultCode.dart';
    import 'package:private_tutor/net/ResultData.dart';
    
    ///http请求管理类,可单独抽取出来
    class HttpRequest {
      static String _baseUrl;
      static const CONTENT_TYPE_JSON = "application/json";
      static const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
      static Map optionParams = {
        "timeoutMs": 15000,
        "token": null,
        "authorizationCode": null,
      };
    
      static setBaseUrl(String baseUrl){
        _baseUrl = baseUrl;
      }
    
      static get(url,param) async{
        return await request(_baseUrl+url, param, null, new Options(method:"GET"));
      }
    
      static post(url,param) async{
        return await request(_baseUrl+url, param, {"Accept": 'application/vnd.github.VERSION.full+json'}, new Options(method: 'POST'));
      }
    
      static delete(url,param) async{
        return await request(_baseUrl+url, param, null, new Options(method: 'DELETE'));
      }
    
      static put(url,param) async{
        return await request(_baseUrl+url, param, null, new Options(method: "PUT", contentType: ContentType.text));
      }
    
      ///发起网络请求
      ///[ url] 请求url
      ///[ params] 请求参数
      ///[ header] 外加头
      ///[ option] 配置
      static request(url, params, Map<String, String> header, Options option, {noTip = false}) async {
    
        //没有网络
        var connectivityResult = await (new Connectivity().checkConnectivity());
        if (connectivityResult == ConnectivityResult.none) {
          return new ResultData(Code.errorHandleFunction(Code.NETWORK_ERROR, "", noTip), false, Code.NETWORK_ERROR);
        }
    
        Map<String, String> headers = new HashMap();
        if (header != null) {
          headers.addAll(header);
        }
    
        //授权码
        if (optionParams["authorizationCode"] == null) {
          var authorizationCode = await getAuthorization();
          if (authorizationCode != null) {
            optionParams["authorizationCode"] = authorizationCode;
          }
        }
    
        headers["Authorization"] = optionParams["authorizationCode"];
        // 设置 baseUrl
        
        if (option != null) {
          option.headers = headers;
        } else{
          option = new Options(method: "get");
          option.headers = headers;
        }
    
        ///超时
        option.connectTimeout = 15000;
    
        Dio dio = new Dio();
        // 添加拦截器
        if (Config.DEBUG) {
          dio.interceptors.add(InterceptorsWrapper(
              onRequest: (RequestOptions options){
                print("\n================== 请求数据 ==========================");
                print("url = ${options.uri.toString()}");
                print("headers = ${options.headers}");
                print("params = ${options.data}");
              },
              onResponse: (Response response){
                print("\n================== 响应数据 ==========================");
                print("code = ${response.statusCode}");
                print("data = ${response.data}");
                print("\n");
              },
              onError: (DioError e){
                print("\n================== 错误响应数据 ======================");
                print("type = ${e.type}");
                print("message = ${e.message}");
                print("stackTrace = ${e.stackTrace}");
                print("\n");
              }
          ));
        }
    
        Response response;
        try {
          response = await dio.request(url, data: params, options: option);
        } on DioError catch (e) {
          // 请求错误处理
          Response errorResponse;
          if (e.response != null) {
            errorResponse = e.response;
          } else {
            errorResponse = new Response(statusCode: 666);
          }
          if (e.type == DioErrorType.CONNECT_TIMEOUT) {
            errorResponse.statusCode = Code.NETWORK_TIMEOUT;
          }
          if (Config.DEBUG) {
            print('请求异常: ' + e.toString());
            print('请求异常 url: ' + url);
          }
          return new ResultData(Code.errorHandleFunction(errorResponse.statusCode, e.message, noTip), false, errorResponse.statusCode);
        }
    
        try {
          if (option.contentType != null && option.contentType.primaryType == "text") {
            return new ResultData(response.data, true, Code.SUCCESS);
          } else {
            var responseJson = response.data;
            if (response.statusCode == 201 && responseJson["token"] != null) {
              optionParams["authorizationCode"] = 'token ' + responseJson["token"];
              await SpUtils.save(Config.TOKEN_KEY, optionParams["authorizationCode"]);
            }
          }
          if (response.statusCode == 200 || response.statusCode == 201) {
            return ResultData(response.data, true, Code.SUCCESS, headers: response.headers);
          }
        } catch (e) {
          print(e.toString() + url);
          return ResultData(response.data, false, response.statusCode, headers: response.headers);
        }
        return new ResultData(Code.errorHandleFunction(response.statusCode, "", noTip), false, response.statusCode);
      }
    
      ///清除授权
      static clearAuthorization() {
        optionParams["authorizationCode"] = null;
        SpUtils.remove(Config.TOKEN_KEY);
      }
    
      ///获取授权token
      static getAuthorization() async {
        String token = await SpUtils.get(Config.TOKEN_KEY);
        if (token == null) {
          String basic = await SpUtils.get(Config.USER_BASIC_CODE);
          if (basic == null) {
            //提示输入账号密码
          } else {
            //通过 basic 去获取token,获取到设置,返回token
            return "Basic $basic";
          }
        } else {
          optionParams["authorizationCode"] = token;
          return token;
        }
      }
    }
    
    

    使用示例

    /// 登录 model
    class LoginModel{
        // 手机号码登录
      static phoneLogin(String phone,String verifyCode) async{
        ResultData response = await HttpRequest.post(Address.phoneLogin, {"phoneNum" : phone,"captcha":verifyCode});
        if(response != null && response.result){
            PhoneLoginEntity phoneLoginEntity = PhoneLoginEntity.fromJson(json.decode(response.data));
            return new DataResult(phoneLoginEntity, true);
        }else{
          return new DataResult(null, false);
        }
      }
    
        // 获取验证码
      static getVerifyCode(String phone) async{
        ResultData response = await HttpRequest.get("${Address.getVerifyCode}?phone=${phone}", null);
    
    //    var response = await HttpRequest.get(Address.getVerifyCode, {"phone":phone});
        if(response != null && response.result){
          VerifyCodeEntity entity = VerifyCodeEntity.fromJson(response.data);
          return new DataResult(entity, true);
        }else{
          return new DataResult(null, false);
        }
      }
    }
    

    作者介绍

    • 陈坚润:广州芦苇科技 APP 团队 Android 开发工程师

    内推信息

    相关文章

      网友评论

        本文标题:Flutter 网络请求框架封装

        本文链接:https://www.haomeiwen.com/subject/yakkvqtx.html