第6章:网络请求与数据处理
“数据是应用的血液,网络是连接世界的桥梁。”
在移动应用开发中,与服务器进行数据交互是必不可少的功能。无论是获取用户信息、提交表单数据,还是上传图片、下载文件,都离不开网络请求。本章将带你深入掌握Flutter中的网络编程技巧。
6.1 网络请求基础概念
6.1.1 什么是HTTP请求?
想象一下,你走进一家餐厅点餐的过程:
- 你告诉服务员想要什么(发送请求)
- 服务员把你的需求传达给厨房(请求到达服务器)
- 厨房准备你的餐点(服务器处理请求)
- 服务员把餐点端给你(接收响应)
HTTP请求就是这样一个过程,只不过是应用与服务器之间的"点餐"过程。
6.1.2 常见的HTTP方法
// GET:获取数据,就像询问菜单
// POST:提交数据,就像下订单
// PUT:更新数据,就像修改订单
// DELETE:删除数据,就像取消订单
// PATCH:部分更新,就像只修改订单中的某个菜品
6.2 HTTP请求的封装与配置
6.2.1 使用原生http库
Flutter提供了基础的http库,但直接使用会让代码变得复杂:
import 'package:http/http.dart' as http;
import 'dart:convert';
class BasicHttpClient {
static const String baseUrl = 'https://api.example.com';
// 基础GET请求
static Future<Map<String, dynamic>> get(String endpoint) async {
try {
final response = await http.get(
Uri.parse('$baseUrl$endpoint'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
);
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('请求失败: ${response.statusCode}');
}
} catch (e) {
throw Exception('网络错误: $e');
}
}
}
6.2.2 为什么选择dio库?
dio库就像是一个功能强大的"网络请求管家",它帮我们处理了很多繁琐的工作:
- 更简洁的API:写更少的代码做更多的事
- 强大的拦截器:统一处理请求和响应
- 自动错误处理:智能的错误重试机制
- 文件操作支持:轻松上传下载文件
- 请求取消:避免内存泄漏
- 缓存支持:提升用户体验
6.3 dio库的高级用法
6.3.1 dio的基本配置
import 'package:dio/dio.dart';
class HttpClient {
static late Dio _dio;
// 初始化dio实例
static void init() {
_dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
sendTimeout: const Duration(seconds: 10),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
_setupInterceptors();
}
static Dio get dio => _dio;
}
6.3.2 创建一个优雅的网络请求封装类
class ApiClient {
late Dio _dio;
ApiClient() {
_dio = Dio();
_setupDio();
}
void _setupDio() {
_dio.options = BaseOptions(
baseUrl: 'https://jsonplaceholder.typicode.com',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {
'Content-Type': 'application/json',
},
);
}
// 通用请求方法
Future<T> request<T>(
String path, {
String method = 'GET',
Map<String, dynamic>? queryParameters,
dynamic data,
Map<String, dynamic>? headers,
required T Function(dynamic) fromJson,
}) async {
try {
final options = Options(
method: method,
headers: headers,
);
final response = await _dio.request(
path,
queryParameters: queryParameters,
data: data,
options: options,
);
return fromJson(response.data);
} on DioException catch (e) {
throw _handleDioError(e);
}
}
// 错误处理
String _handleDioError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return '连接超时,请检查网络';
case DioExceptionType.receiveTimeout:
return '接收数据超时,请重试';
case DioExceptionType.badResponse:
return '服务器错误:${e.response?.statusCode}';
case DioExceptionType.cancel:
return '请求已取消';
default:
return '网络错误:${e.message}';
}
}
}
6.4 RESTful API接口调用
6.4.1 理解RESTful API
RESTful API就像是一套标准的"服务规范":
- 资源导向:把数据看作资源,每个资源都有唯一的URL
- HTTP方法语义化:用不同的HTTP方法表示不同的操作
- 状态码标准化:用HTTP状态码表示操作结果
6.4.2 实现完整的CRUD操作
class UserService {
final ApiClient _apiClient = ApiClient();
// 获取用户列表 (GET)
Future<List<User>> getUsers() async {
return await _apiClient.request<List<User>>(
'/users',
fromJson: (data) => (data as List)
.map((item) => User.fromJson(item))
.toList(),
);
}
// 获取单个用户 (GET)
Future<User> getUser(int id) async {
return await _apiClient.request<User>(
'/users/$id',
fromJson: (data) => User.fromJson(data),
);
}
// 创建用户 (POST)
Future<User> createUser(User user) async {
return await _apiClient.request<User>(
'/users',
method: 'POST',
data: user.toJson(),
fromJson: (data) => User.fromJson(data),
);
}
// 更新用户 (PUT)
Future<User> updateUser(int id, User user) async {
return await _apiClient.request<User>(
'/users/$id',
method: 'PUT',
data: user.toJson(),
fromJson: (data) => User.fromJson(data),
);
}
// 删除用户 (DELETE)
Future<void> deleteUser(int id) async {
await _apiClient.request<void>(
'/users/$id',
method: 'DELETE',
fromJson: (data) => null,
);
}
}
6.5 JSON数据序列化与反序列化
6.5.1 理解JSON序列化
JSON序列化就像是"翻译官"的工作:
- 序列化:把Dart对象翻译成JSON字符串,方便网络传输
- 反序列化:把JSON字符串翻译回Dart对象,方便程序使用
6.5.2 手动序列化(适合简单场景)
class User {
final int id;
final String name;
final String email;
final String? avatar;
User({
required this.id,
required this.name,
required this.email,
this.avatar,
});
// 从JSON创建对象(反序列化)
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
avatar: json['avatar'] as String?,
);
}
// 转换为JSON(序列化)
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
if (avatar != null) 'avatar': avatar,
};
}
String toString() {
return 'User{id: $id, name: $name, email: $email}';
}
}
6.5.3 使用json_annotation(推荐方式)
首先添加依赖:
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
json_serializable: ^6.7.1
build_runner: ^2.4.7
然后创建模型类:
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
()
class User {
final int id;
final String name;
final String email;
(name: 'avatar_url')
final String? avatarUrl;
(name: 'created_at')
final DateTime? createdAt;
User({
required this.id,
required this.name,
required this.email,
this.avatarUrl,
this.createdAt,
});
factory User.fromJson(Map<String, dynamic> json) =&