如何在 Flutter 上使用 cookie 发出 http 请求?
Posted
技术标签:
【中文标题】如何在 Flutter 上使用 cookie 发出 http 请求?【英文标题】:How do I make an http request using cookies on flutter? 【发布时间】: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;
【问题讨论】:
【参考方案1】:这是一个如何获取会话 cookie 并在后续请求中返回它的示例。您可以轻松调整它以返回多个 cookie。创建一个Session
类,并通过它路由你所有的GET
s 和POST
s。
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 是在分号(如果有的话)后面切掉一点 - 请参阅此处的讨论:***.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
【讨论】:
以上是关于如何在 Flutter 上使用 cookie 发出 http 请求?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Flutter 中使用 JSON 正文发出 http DELETE 请求?