'# Flutter学习9 - http 中 get/post 请求示例
一、背景与问题
在Flutter开发中,与后端API进行数据交互是核心需求之一。http库作为Flutter官方推荐的网络请求库,提供了对GET/POST等HTTP方法的完整支持。然而,许多开发者在使用时容易陷入误区:例如误用GET方法传递敏感数据、未处理网络异常、忽略响应格式校验等。
本文将深入解析http库的工作原理,通过三个代码示例和一个完整案例,展示如何安全、高效地进行网络请求。我们将重点分析:
- HTTP协议的底层实现机制
- Dart语言的异步处理模型
- 常见错误场景及解决方案
- 性能优化策略
- 安全风险防范措施
二、基本原理
1. HTTP协议原理
HTTP是基于TCP/IP的无状态协议,通过请求行、请求头和请求体三个部分构成请求。关键字段包括:
GET/POST:请求方法Content-Type:数据格式(如application/json)Accept:客户端可接受的响应格式Authorization:认证信息
在Flutter中,http库通过HttpClient底层使用dart:io的http模块实现网络通信,采用异步方式处理请求。
2. Dart异步模型
Dart通过Future和async/await实现异步编程。http库返回Future<HttpResponse>对象,开发者需要通过await或.then()处理响应。
三、环境准备
# 创建Flutter项目
flutter create http_demo
cd http_demo
# 添加依赖(如果未包含)
# 在pubspec.yaml中添加(若需使用更高级功能)
dependencies:
http: ^0.13.5四、核心实现
1. GET请求示例
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<void> fetchUserData(String userId) async {
final url = Uri.https('jsonplaceholder.typicode.com', '/users/$userId');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
final data = json.decode(response.body);
print('User data: $data');
} else {
throw Exception('Failed to load user data');
}
} catch (e) {
print('Error: $e');
}
}关键点解析:
- 使用
Uri.https构建安全URL - 检查响应状态码(200表示成功)
- 使用
json.decode解析JSON响应 - 异常处理需要捕获所有可能的错误
2. POST请求示例
Future<void> submitData(String name, String email) async {
final url = Uri.https('jsonplaceholder.typicode.com', '/posts');
final response = await http.post(
url,
headers: {'Content-Type': 'application/json'},
body: json.encode({
'title': 'Flutter Post',
'body': 'This is a test post',
'userId': 1,
'name': name,
'email': email
}),
);
if (response.statusCode == 201) {
print('Post submitted successfully');
} else {
throw Exception('Failed to submit post');
}
}关键点解析:
- 使用
http.post发送POST请求 - 必须设置
Content-Type头 - 使用
json.encode将Map转换为JSON字符串 - 状态码201表示创建成功
3. 文件上传示例
Future<void> uploadFile(String filePath) async {
final url = Uri.https('jsonplaceholder.typicode.com', '/posts');
final request = http.MultipartRequest('POST', url);
final file = await http.MultipartFile.fromPath('photo', filePath);
request.files.add(file);
request.headers['Content-Type'] = 'multipart/form-data';
final response = await request.send();
if (response.statusCode == 201) {
print('File uploaded successfully');
} else {
throw Exception('Failed to upload file');
}
}关键点解析:
- 使用
MultipartRequest处理文件上传 MultipartFile.fromPath创建文件对象- 必须设置
multipart/form-data内容类型 - 多文件上传需要添加多个
MultipartFile实例
五、完整案例
1. 登录页面实现
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String _username = '';
String _password = '';
Future<void> _login() async {
if (_formKey.currentState!.validate()) {
try {
final response = await http.post(
Uri.parse('https://api.example.com/login'),
headers: {'Content-Type': 'application/json'},
body: json.encode({
'username': _username,
'password': _password
}),
);
if (response.statusCode == 200) {
final token = json.decode(response.body)['token'];
// 保存token到SharedPreferences
print('Login successful, token: $token');
} else {
throw Exception('Login failed');
}
} catch (e) {
print('Error: $e');
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Username'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter username';
}
return null;
},
onSaved: (value) => _username = value!,
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter password';
}
return null;
},
onSaved: (value) => _password = value!,
obscureText: true,
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _login,
child: Text('Login'),
),
],
),
),
),
);
}
}关键点解析:
- 使用
Form和TextFormField构建表单 - 验证表单字段有效性
- 使用
json.encode将登录信息发送 - 处理响应并保存token
- 处理各种可能的错误
六、源码解析
1. http库源码结构
http库的核心类包括:
HttpClient:管理HTTP连接Request:表示HTTP请求Response:表示HTTP响应Client:封装底层连接逻辑
关键流程如下:
- 创建
HttpClient实例 - 构造
Request对象(GET/POST等) - 设置请求头和请求体
- 发送请求并获取
Response - 处理响应数据
2. 异步处理机制
http库内部使用Future和Stream实现异步处理,关键代码如下:
Future<HttpResponse> get(Uri url, {Map<String, String>? headers}) {
final request = new GetRequest(url, headers: headers);
return new HttpClient().send(request);
}七、进阶使用
1. 超时处理
final client = http.Client();
final response = await client.get(url, timeout: Duration(seconds: 10));2. 重试机制
Future<void> retryRequest(String url, int maxRetries) async {
int retries = 0;
while (retries < maxRetries) {
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
return;
}
} catch (e) {
retries++;
await Future.delayed(Duration(seconds: 1));
}
}
throw Exception('Request failed after $maxRetries retries');
}3. 请求拦截器
final client = http.Client();
client.addInterceptor((request, next) {
request.headers['Authorization'] = 'Bearer ${token}';
next(request);
});八、性能与工程实践
1. 性能优化策略
| 优化措施 | 说明 |
|---|---|
| 缓存策略 | 使用SharedPreferences缓存常见数据 |
| 压缩数据 | 使用GZIP压缩传输数据 |
| 并行请求 | 使用Future.wait处理多个并发请求 |
| 错误重试 | 设置合理的重试机制 |
| 连接复用 | 使用HttpClient的连接池功能 |
2. 异常处理规范
try {
await fetchUserData('1');
} catch (e) {
if (e is Exception) {
// 处理通用异常
} else if (e is http.ClientException) {
// 处理网络异常
}
}3. 安全实践
- 始终使用HTTPS
- 敏感数据加密传输(如使用
encrypt库) - 使用
flutter_secure_storage存储敏感信息 - 防止CSRF攻击(需后端配合)
九、常见问题与踩坑
1. 常见错误场景
| 问题 | 解决方案 |
|---|---|
| 忽略错误处理 | 使用try-catch块捕获所有异常 |
| 未设置Content-Type | 在POST请求中设置Content-Type |
| 未处理响应格式 | 使用json.decode解析JSON响应 |
| 跨域问题 | 需要后端配置CORS |
| 超时未处理 | 设置合理的超时时间 |
2. 典型错误示例
// 错误示例:未处理异常
await http.get(Uri.parse('https://example.com'));改进方案:
try {
await http.get(Uri.parse('https://example.com'));
} catch (e) {
print('Request failed: $e');
}3. 常见坑点
- 使用
http库时未处理ClientException异常 - 忘记在POST请求中设置
Content-Type头 - 在GET请求中传递敏感数据
- 未对响应数据进行校验
- 未处理网络状态变化(如断网)
十、最佳实践
1. 推荐方案
| 场景 | 推荐做法 |
|---|---|
| 获取数据 | 使用GET方法 |
| 提交数据 | 使用POST方法 |
| 文件上传 | 使用multipart/form-data |
| 身份认证 | 使用Bearer Token |
| 错误处理 | 使用try-catch并区分异常类型 |
2. 代码规范建议
- 始终使用
async/await处理异步请求 - 使用
json.decode解析JSON响应 - 使用
Uri.https构建安全URL - 使用
SharedPreferences存储敏感信息 - 使用
http.Client复用连接
十一、总结
本文深入解析了Flutter中使用http库进行网络请求的原理与实践,重点分析了GET/POST请求的实现方式、常见错误、性能优化、安全风险等关键点。通过三个代码示例和一个完整案例,展示了如何在实际开发中正确使用网络请求功能。
关键收获包括:
- 理解HTTP协议的基本原理
- 掌握Dart的异步编程模型
- 熟悉
http库的核心使用方法 - 了解常见错误场景及解决方案
- 掌握性能优化和安全实践
在实际开发中,应根据具体场景选择合适的请求方法,始终关注安全性、错误处理和性能优化。对于涉及敏感数据的场景,建议使用更高级的库如dio或http_interceptor来增强功能。