How do I make an http request using cookies on flutter?

Viewed 51018

I'd like to make an http request to a remote server while properly handling cookies (eg. storing cookies sent by the server, and sending those cookies when I make subsequent requests). It'd be nice to preserve any and all cookies

for http request I am using

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;
}
6 Answers

Here's an example of how to grab a session cookie and return it on subsequent requests. You could easily adapt it to return multiple cookies. Make a Session class and route all your GETs and POSTs through it.

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);
    }
  }
}

I have improved the Richard Heap's solution to be capable to process multiple 'Set-cookies' and multiple cookies.

In my case, the server returns multiples 'Set-cookies'. The http package concatenate all the set-cookies headers in one header and separate it by comma (',').

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);
    });
  }
}

I've published a small flutter library called requests to assist with cookie-aware http requests.

dependencies:
  requests: ^3.0.1

Usage:

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'])

find out more about requests

I migrated larly's answer to nullsafety. Also added 'put' function.

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);
    });
  }
}

If it wasn't a problem, I use dio and cookiejar.

Just add these dependencies in your pubspec.yaml:

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

Here's an example to use it. Make sure you run it as a flutter script, and not a dart script.

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);
}

an example output:

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

I found best solution to handle cookies for redirect also

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;
      }
    });
  }
}
Related