如何将Python中基于Session的Duolingo API请求迁移到Flutter实现
问题解决思路
你的Python代码能正常运行是因为requests.Session()会自动维护同一会话内的Cookie:登录接口返回的认证Cookie会被自动存储,后续同会话内的所有请求都会自动携带这些Cookie,服务端通过Cookie识别你的登录状态。
而你当前用的Flutter官方http包默认是无状态的,两次请求完全独立,登录接口返回的Cookie不会被自动带到后续的GET请求里,服务端识别不到登录状态,就返回了错误页面。
方案1:手动处理Cookie(适合快速验证逻辑)
你可以手动提取登录响应里的Cookie,在发起GET请求时主动携带:
import 'dart:convert'; import 'package:http/http.dart' as http; // 登录请求 final loginResponse = await http.post( Uri.parse('https://www.duolingo.com/2017-06-30/login?fields='), headers: <String, String>{ 'Content-Type': 'application/json', }, body: jsonEncode(<String, String>{ 'identifier': '你的用户名', 'password': '你的密码', }), ); if (loginResponse.statusCode == 200) { print("登录成功"); // 提取响应头中的Set-Cookie,过滤属性只保留key=value部分,多个Cookie用分号分隔 String rawCookie = loginResponse.headers['set-cookie']!; List<String> cookies = rawCookie.split(RegExp(r',\s*')).map((cookieStr) { return cookieStr.split(';')[0]; }).toList(); String finalCookie = cookies.join('; '); // 发起GET请求时携带Cookie final vocabResponse = await http.get( Uri.parse("https://www.duolingo.com/vocabulary/overview"), headers: <String, String>{ 'Cookie': finalCookie, }, ); // 打印返回的词汇数据 print(vocabResponse.body); }
方案2:用会话管理工具自动维护Cookie(适合长期使用)
如果后续还要发更多同域名的请求,手动处理Cookie太麻烦,可以用dio网络库搭配dio_cookie_manager插件,实现和Pythonrequests.Session一样的自动会话管理能力:
- 先在
pubspec.yaml里添加依赖:
dependencies: dio: ^5.4.0 dio_cookie_manager: ^3.1.0 cookie_jar: ^4.0.0
- 代码示例:
import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:cookie_jar/cookie_jar.dart'; void fetchDuolingoVocab() async { final dio = Dio(); final cookieJar = CookieJar(); // 给dio添加Cookie管理拦截器,自动存储、携带Cookie dio.interceptors.add(CookieManager(cookieJar)); try { // 登录请求,返回的Cookie会被自动保存 await dio.post( 'https://www.duolingo.com/2017-06-30/login?fields=', data: jsonEncode(<String, String>{ 'identifier': '你的用户名', 'password': '你的密码', }), options: Options( headers: {'Content-Type': 'application/json'}, ), ); // 直接发起GET请求,Cookie会被自动携带 final vocabResponse = await dio.get( "https://www.duolingo.com/vocabulary/overview" ); print(vocabResponse.data); } catch (e) { print('请求出错:$e'); } }
注意事项
- 不要在代码中硬编码用户名密码,建议通过环境变量或用户输入的方式获取
- Duolingo的非公开接口可能随时调整,若后续请求失败可以先校验接口地址和请求参数是否有变化
内容的提问来源于stack exchange,提问作者Ziad Osman
相关产品推荐
相关产品推荐

