【问题标题】:How do I make an http request using cookies on flutter?如何在 Flutter 上使用 cookie 发出 http 请求?
【发布时间】:2019-02-13 21:39:22
【问题描述】:

我想在正确处理 cookie 的同时向远程服务器发出 http 请求(例如,存储服务器发送的 cookie,并在我发出后续请求时发送这些 cookie)。最好保留所有 cookie

对于我正在使用的 http 请求

static Future<Map> postData(Map data) async {
  http.Response res = await http.post(url, body: data); // post api call
  Map data = JSON.decode(res.body);
  return data;
}

【问题讨论】:

    标签: android http cookies flutter


    【解决方案1】:

    这是一个如何获取会话 cookie 并在后续请求中返回它的示例。您可以轻松调整它以返回多个 cookie。创建一个Session 类,并通过它路由你所有的GETs 和POSTs。

    class Session {
      Map<String, String> headers = {};
    
      Future<Map> get(String url) async {
        http.Response response = await http.get(url, headers: headers);
        updateCookie(response);
        return json.decode(response.body);
      }
    
      Future<Map> post(String url, dynamic data) async {
        http.Response response = await http.post(url, body: data, headers: headers);
        updateCookie(response);
        return json.decode(response.body);
      }
    
      void updateCookie(http.Response response) {
        String rawCookie = response.headers['set-cookie'];
        if (rawCookie != null) {
          int index = rawCookie.indexOf(';');
          headers['cookie'] =
              (index == -1) ? rawCookie : rawCookie.substring(0, index);
        }
      }
    }
    

    【讨论】:

    • 只是问你将如何在小部件中调用它。
    • 谢谢,工作得很好!我使用了这个解决方案,我只是将类更改为单例,这样我就可以在我的小部件中访问一个实例
    • 为什么只得到 ' 之前的第一个子字符串? ' 的饼干?例如,在这个 cookie 中:“OCENTRIC_COOKIE=1; path=/;Secure;HttpOnly”,在 ' 之后是什么; ' 不重要吗?
    • 根据 RFC,所有标头都不区分大小写,尽管许多服务器不兼容。为了便于比较,Dart 强制它们全部小写。
    • @daraul 是在分号(如果有的话)后面切掉一点 - 请参阅此处的讨论:stackoverflow.com/questions/50299253/…
    【解决方案2】:

    我改进了 Richard Heap 的解决方案,使其能够处理多个“Set-cookies”和多个 cookie。

    在我的例子中,服务器返回多个“Set-cookies”。 http 包将所有 set-cookies 标头连接到一个标头中,并用逗号 (',') 分隔。

    class NetworkService {
    
      final JsonDecoder _decoder = new JsonDecoder();
      final JsonEncoder _encoder = new JsonEncoder();
    
      Map<String, String> headers = {"content-type": "text/json"};
      Map<String, String> cookies = {};
    
      void _updateCookie(http.Response response) {
        String allSetCookie = response.headers['set-cookie'];
    
        if (allSetCookie != null) {
    
          var setCookies = allSetCookie.split(',');
    
          for (var setCookie in setCookies) {
            var cookies = setCookie.split(';');
    
            for (var cookie in cookies) {
              _setCookie(cookie);
            }
          }
    
          headers['cookie'] = _generateCookieHeader();
        }
      }
    
      void _setCookie(String rawCookie) {
        if (rawCookie.length > 0) {
          var keyValue = rawCookie.split('=');
          if (keyValue.length == 2) {
            var key = keyValue[0].trim();
            var value = keyValue[1];
    
            // ignore keys that aren't cookies
            if (key == 'path' || key == 'expires')
              return;
    
            this.cookies[key] = value;
          }
        }
      }
    
      String _generateCookieHeader() {
        String cookie = "";
    
        for (var key in cookies.keys) {
          if (cookie.length > 0)
            cookie += ";";
          cookie += key + "=" + cookies[key];
        }
    
        return cookie;
      }
    
      Future<dynamic> get(String url) {
        return http.get(url, headers: headers).then((http.Response response) {
          final String res = response.body;
          final int statusCode = response.statusCode;
    
          _updateCookie(response);
    
          if (statusCode < 200 || statusCode > 400 || json == null) {
            throw new Exception("Error while fetching data");
          }
          return _decoder.convert(res);
        });
      }
    
      Future<dynamic> post(String url, {body, encoding}) {
        return http
            .post(url, body: _encoder.convert(body), headers: headers, encoding: encoding)
            .then((http.Response response) {
          final String res = response.body;
          final int statusCode = response.statusCode;
    
          _updateCookie(response);
    
          if (statusCode < 200 || statusCode > 400 || json == null) {
            throw new Exception("Error while fetching data");
          }
          return _decoder.convert(res);
        });
      }
    }
    

    【讨论】:

    • 谢谢,您的回答帮助我检索了多个 cookie
    • 感谢这对我有帮助。在 '=' 上拆分 rawCookie 时有一个小错误;当拆分中有空字符串时,它不起作用。以下修复帮助了我。 rawCookie.split('=').where((s) => s.isNotEmpty).toList(growable: false)
    • 在比较路径和过期时也忽略大小写 - if (key.toLowerCase() != 'path' && key.toLowerCase() != 'expires') { return {key: value}; }
    • 当cookie的值部分有任何'='时,答案不考虑。我必须更改 _setCookie 方法: if (rawCookie.length > 0) { int idx = rawCookie.indexOf("="); if (idx >= 0) { var key = rawCookie.substring(0, idx).trim(); var value = rawCookie.substring(idx+1).trim(); if (key == 'path' || key == 'expires' || key == 'domain' || key == 'SameSite') 返回;饼干[键] =值; } }
    【解决方案3】:

    我发布了一个名为 requests 的小型 Flutter 库来协助处理可识别 cookie 的 http 请求。

    dependencies:
      requests: ^3.0.1
    

    用法:

    import 'package:requests/requests.dart';
    
    // ...
    
    // this will persist cookies
    var r1 = await Requests.post("https://example.com/api/v1/login", json: {"username":"...", "password":"..."} ); 
    r1.raiseForStatus();
    
    // this will re-use the persisted cookies
    var r2 = await Requests.get("https://example.com/api/v1/stuff"); 
    r2.raiseForStatus();
    print(r2.json()['id'])
    

    了解更多关于requests

    【讨论】:

    • 非常感谢先生!你的包裹非常适合我!
    • 嗨,我正在使用来自这里 github.com/zino-app/graphql-flutter 的 graphql 服务器和 flutter_graphql 包。关于如何使用这个包的任何想法
    • @Jossef 嗨,有没有办法在不终止第一个请求的情况下发送第二个请求?我在处理过期时间“会话”的 cookie 时遇到问题
    • @CharukaHS 请打开一个问题 -> github.com/jossef/requests/issues 添加您的请求响应详细示例
    • 好像有旧的依赖不能用这个
    【解决方案4】:

    我也找到了处理重定向 cookie 的最佳解决方案

    import 'dart:convert';
    import 'dart:io';
    
    class CustomHTTPClient{
      final HttpClient _client = new HttpClient();
      Map<String, String> _cookies = Map();
    
      CustomHTTPClient(){
        _client.connectionTimeout = Duration(seconds: 10);
      }
    
      Future<String> get(String url, {int maxRedirect = 3}) async {
        final parsedUrl = Uri.parse(url);
        return await _client.getUrl(parsedUrl)
            .then((HttpClientRequest request) {
          request.followRedirects = false;
          _beforeRequest(request);
          return request.close();
        }).then((HttpClientResponse response) async {
          _afterResponse(response);
          if(response.isRedirect && maxRedirect > 0){
            return await response.drain().then((value) => get(parsedUrl.resolve(response.headers.value('location')).toString(), maxRedirect: maxRedirect - 1));
          }
          return response.transform(utf8.decoder).join();
        }).catchError((error, stack){
          print(error);print(stack);
        });
      }
    
      void _beforeRequest(HttpClientRequest request){
        request.headers.set(HttpHeaders.acceptEncodingHeader, 'gzip, deflate, br');
    
        // Set cookie
        final String rawCookies = _cookies.keys.map((String name) => '$name=${_cookies[name]}').join('; ');
        if(rawCookies.isNotEmpty) request.headers.set(HttpHeaders.cookieHeader, rawCookies);
      }
    
      void _afterResponse(HttpClientResponse response){
        response.headers.forEach((String name, List<String> values){
          if(name == 'set-cookie'){ // Get cookies for next request
            values.forEach((String rawCookie){
              try{
                Cookie cookie = Cookie.fromSetCookieValue(rawCookie);
                _cookies[cookie.name] = cookie.value;
              } catch(e){
                final List<String> cookie = rawCookie.split(';')[0].split('=');
                _cookies[cookie[0]] = cookie[1];
              }
            });
            return false;
          }
        });
      }
    }
    

    【讨论】:

      【解决方案5】:

      我已将 larly's answer 迁移到 nullsafety。 还增加了'put'功能。

      import 'dart:convert';
      import 'package:http/http.dart' as http;
      
      
      class NetworkService {
        final JsonDecoder _decoder = const JsonDecoder();
        final JsonEncoder _encoder = const JsonEncoder();
      
        Map<String, String> headers = {"content-type": "application/json"};
        Map<String, String> cookies = {};
      
        void _updateCookie(http.Response response) {
          String? allSetCookie = response.headers['set-cookie'];
      
          if (allSetCookie != null) {
            var setCookies = allSetCookie.split(',');
      
            for (var setCookie in setCookies) {
              var cookies = setCookie.split(';');
      
              for (var cookie in cookies) {
                _setCookie(cookie);
              }
            }
      
            headers['cookie'] = _generateCookieHeader();
          }
        }
      
        void _setCookie(String? rawCookie) {
          if (rawCookie != null) {
            var keyValue = rawCookie.split('=');
            if (keyValue.length == 2) {
              var key = keyValue[0].trim();
              var value = keyValue[1];
      
              // ignore keys that aren't cookies
              if (key == 'path' || key == 'expires') return;
      
              cookies[key] = value;
            }
          }
        }
      
        String _generateCookieHeader() {
          String cookie = "";
      
          for (var key in cookies.keys) {
            if (cookie.isNotEmpty) cookie += ";";
            cookie += key + "=" + cookies[key]!;
          }
      
          return cookie;
        }
      
        Future<dynamic> get(String url) {
          return http.get(Uri.parse(url), headers: headers).then((http.Response response) {
            final String res = response.body;
            final int statusCode = response.statusCode;
      
            _updateCookie(response);
      
            if (statusCode < 200 || statusCode > 400) {
              throw Exception("Error while fetching data");
            }
            return _decoder.convert(res);
          });
        }
      
        Future<dynamic> post(String url, {body, encoding}) {
          return http.post(Uri.parse(url), body: _encoder.convert(body), headers: headers, encoding: encoding).then((http.Response response) {
            final String res = response.body;
            final int statusCode = response.statusCode;
      
            _updateCookie(response);
      
            if (statusCode < 200 || statusCode > 400) {
              throw Exception("Error while fetching data");
            }
            return _decoder.convert(res);
          });
        }
      
        Future<dynamic> put(String url, {body, encoding}) {
          return http.put(Uri.parse(url), body: _encoder.convert(body), headers: headers, encoding: encoding).then((http.Response response) {
            final String res = response.body;
            final int statusCode = response.statusCode;
      
            _updateCookie(response);
      
            if (statusCode < 200 || statusCode > 400) {
              throw Exception("Error while fetching data");
            }
            return _decoder.convert(res);
          });
        }
      }
      

      【讨论】:

        【解决方案6】:

        如果没有问题,我会使用 dio 和 cookiejar。

        只需在您的pubspec.yaml 中添加这些依赖项:

        dependencies:
          dio: ^4.0.4
          dio_cookie_manager: ^2.0.0
          cookie_jar: ^3.0.1
        

        这是一个使用它的示例。确保将其作为颤振脚本而不是 dart 脚本运行。

        import 'package:cookie_jar/cookie_jar.dart';
        import 'package:dio/dio.dart';
        import 'package:dio_cookie_manager/dio_cookie_manager.dart';
        
        
        void main() async {
          var dio =  Dio(BaseOptions(
              connectTimeout: 10000,  // in ms
              receiveTimeout: 10000,
              sendTimeout: 10000,
              responseType: ResponseType.plain,
              followRedirects: false,
              validateStatus: (status) { return true; }
          ));   // some dio configurations
        
          dio.interceptors.add(CookieManager(CookieJar()));
        
          var firstResponse = await dio.get(
              "https://somewebsite.com/get_login_info");
          print(firstResponse.data);
        
          var loginResponse = await dio.post(
              "https://somewebsite.com/login",
              data: FormData.fromMap(
                  {
                    'username': 'YourUsername',
                    'password': 'YourPassword',
                  }
              ));  // cookies are automatically saved
          print(loginResponse.statusCode);
        
          var nextResponse = await dio.get(
              "https://somewebsite.com/get_login_info");
          print(nextResponse.data);
        }
        

        示例输出:

        {"is_logged_in": 0}
        302
        {"is_logged_in": 1}
        

        【讨论】:

          猜你喜欢
          • 2019-02-15
          • 2010-10-15
          • 2019-12-11
          • 2020-09-13
          • 2019-10-02
          • 2019-12-05
          • 1970-01-01
          • 2019-10-20
          • 1970-01-01
          相关资源
          最近更新 更多