HTTP响应处理 - 鸿蒙Flutter响应解析封装应用
·




概述
HTTP响应是服务器对客户端请求的答复,包含了状态码、响应头和响应体三部分。正确处理HTTP响应是网络编程的关键环节,涉及状态码判断、响应头解析、响应体转换等多个方面。
本章将详细介绍HTTP响应的处理方法,包括响应对象属性、状态码分类、JSON解析、二进制数据处理以及响应封装等内容。
1. 响应对象属性
http包的Response对象包含了服务器返回的所有信息,以下是常用属性:
| 属性 | 类型 | 说明 |
|---|---|---|
| statusCode | int |
HTTP状态码,表示请求处理结果 |
| headers | Map<String, String> |
响应头集合,包含元数据信息 |
| body | String |
响应体内容,文本格式 |
| bodyBytes | Uint8List |
响应体字节数据,用于二进制内容 |
| isRedirect | bool |
是否为重定向响应 |
| persistentConnection | bool |
是否保持持久连接 |
| contentLength | int |
响应内容长度 |
1.1 响应对象的基本使用
import 'package:http/http.dart' as http;
Future<void> handleResponse() async {
final response = await http.get(Uri.parse('https://api.example.com/data'));
print('状态码: ${response.statusCode}');
print('响应体: ${response.body}');
print('Content-Type: ${response.headers['content-type']}');
}
2. HTTP状态码处理
状态码是HTTP响应中最重要的信息,用于表示请求的处理结果。
2.1 状态码分类
| 状态码范围 | 类别 | 说明 |
|---|---|---|
| 1xx | 信息性响应 | 请求已接收,服务器正在处理 |
| 2xx | 成功 | 请求成功处理 |
| 3xx | 重定向 | 需要进一步操作才能完成请求 |
| 4xx | 客户端错误 | 请求有问题,服务器无法处理 |
| 5xx | 服务器错误 | 服务器处理请求时发生错误 |
2.2 常用状态码
| 状态码 | 名称 | 说明 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功 |
| 204 | No Content | 请求成功但无响应体 |
| 301 | Moved Permanently | 永久重定向 |
| 302 | Found | 临时重定向 |
| 400 | Bad Request | 请求参数错误 |
| 401 | Unauthorized | 未授权,需要认证 |
| 403 | Forbidden | 禁止访问 |
| 404 | Not Found | 资源不存在 |
| 408 | Request Timeout | 请求超时 |
| 409 | Conflict | 请求与服务器状态冲突 |
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用 |
2.3 状态码处理示例
Future<void> handleStatusCode() async {
final response = await http.get(Uri.parse('https://api.example.com/data'));
switch (response.statusCode) {
case 200:
print('请求成功');
break;
case 201:
print('资源创建成功');
break;
case 400:
print('请求参数错误');
break;
case 401:
print('未授权,需要登录');
break;
case 403:
print('禁止访问');
break;
case 404:
print('资源不存在');
break;
case 500:
print('服务器内部错误');
break;
default:
print('未知错误: ${response.statusCode}');
}
}
2.4 状态码判断模式
实际开发中,通常采用范围判断而非逐个判断:
if (response.statusCode >= 200 && response.statusCode < 300) {
// 成功
} else if (response.statusCode >= 400 && response.statusCode < 500) {
// 客户端错误
} else if (response.statusCode >= 500) {
// 服务器错误
}
3. 响应头处理
响应头包含了服务器返回的元数据信息,如内容类型、内容长度、日期等。
3.1 获取响应头
Future<void> handleResponseHeaders() async {
final response = await http.get(Uri.parse('https://api.example.com/data'));
String contentType = response.headers['content-type'] ?? 'unknown';
String contentLength = response.headers['content-length'] ?? 'unknown';
String date = response.headers['date'] ?? 'unknown';
String server = response.headers['server'] ?? 'unknown';
print('Content-Type: $contentType');
print('Content-Length: $contentLength');
print('Date: $date');
print('Server: $server');
}
3.2 常用响应头
| 响应头 | 说明 |
|---|---|
| Content-Type | 响应内容类型 |
| Content-Length | 响应内容长度 |
| Date | 响应时间 |
| Server | 服务器标识 |
| Cache-Control | 缓存控制策略 |
| ETag | 资源版本标识 |
| Location | 重定向目标地址 |
| Set-Cookie | 设置Cookie |
4. JSON响应解析
现代API大多返回JSON格式数据,需要将响应体解析为Dart对象。
4.1 基本JSON解析
import 'dart:convert';
Future<Map<String, dynamic>> parseJsonResponse() async {
final response = await http.get(Uri.parse('https://api.example.com/user'));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('请求失败: ${response.statusCode}');
}
}
4.2 列表响应解析
Future<List<dynamic>> parseListResponse() async {
final response = await http.get(Uri.parse('https://api.example.com/users'));
if (response.statusCode == 200) {
return jsonDecode(response.body) as List<dynamic>;
} else {
throw Exception('请求失败');
}
}
4.3 解析为自定义对象
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'],
email: json['email'],
);
}
}
Future<User> fetchUser(String userId) async {
final response = await http.get(Uri.parse('https://api.example.com/users/$userId'));
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
} else {
throw Exception('获取用户失败');
}
}
4.4 使用json_serializable
对于复杂对象,推荐使用json_serializable包自动生成序列化代码。
pubspec.yaml配置:
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.6
json_serializable: ^6.7.1
使用示例:
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
()
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
生成序列化代码:
dart run build_runner build
5. 二进制响应处理
对于图片、文件等二进制数据,需要使用bodyBytes属性。
5.1 获取二进制数据
import 'dart:typed_data';
Future<Uint8List> fetchBinaryData() async {
final response = await http.get(Uri.parse('https://api.example.com/image'));
if (response.statusCode == 200) {
return response.bodyBytes;
} else {
throw Exception('获取二进制数据失败');
}
}
5.2 显示网络图片
import 'package:flutter/material.dart';
class NetworkImageWidget extends StatelessWidget {
Widget build(BuildContext context) {
return FutureBuilder<Uint8List>(
future: fetchBinaryData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.memory(snapshot.data!);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}
5.3 文件下载
import 'dart:io';
Future<void> downloadFile(String url, String savePath) async {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final file = File(savePath);
await file.writeAsBytes(response.bodyBytes);
print('文件下载成功');
} else {
throw Exception('文件下载失败');
}
}
6. 响应封装
为了统一处理响应,通常会封装一个响应类。
6.1 统一响应类
class ApiResponse<T> {
final int statusCode;
final T? data;
final String? message;
final bool success;
ApiResponse.success({required this.statusCode, required this.data})
: message = null,
success = true;
ApiResponse.error({required this.statusCode, required this.message})
: data = null,
success = false;
}
6.2 统一响应处理函数
Future<ApiResponse<T>> handleResponse<T>({
required Future<http.Response> request,
required T Function(Map<String, dynamic>) parser,
}) async {
try {
final response = await request;
if (response.statusCode >= 200 && response.statusCode < 300) {
Map<String, dynamic> json = jsonDecode(response.body);
return ApiResponse.success(
statusCode: response.statusCode,
data: parser(json),
);
} else {
return ApiResponse.error(
statusCode: response.statusCode,
message: '请求失败',
);
}
} catch (e) {
return ApiResponse.error(
statusCode: -1,
message: e.toString(),
);
}
}
6.3 使用封装的响应
Future<void> fetchUserData() async {
final response = await handleResponse<User>(
request: http.get(Uri.parse('https://api.example.com/users/1')),
parser: (json) => User.fromJson(json),
);
if (response.success) {
print('用户名称: ${response.data?.name}');
} else {
print('错误: ${response.message}');
}
}
7. 响应处理最佳实践
7.1 状态码检查
始终检查状态码,不要假设请求总是成功:
// 错误做法
final response = await http.get(uri);
var data = jsonDecode(response.body);
// 正确做法
final response = await http.get(uri);
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
} else {
// 处理错误
}
7.2 异常捕获
使用try-catch捕获可能的异常:
try {
final response = await http.get(uri);
// 处理响应
} catch (e) {
// 处理网络异常
}
7.3 JSON解析安全
在解析JSON时,处理可能的格式错误:
try {
var json = jsonDecode(response.body);
} catch (e) {
throw Exception('JSON解析失败: $e');
}
7.4 数据类型转换
确保正确处理JSON中的数据类型:
// 错误做法
int id = json['id'];
// 正确做法
int id = json['id'] as int;
// 或
int id = int.parse(json['id'].toString());
8. 实践示例:完整的响应处理
import 'package:http/http.dart' as http;
import 'dart:convert';
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
email: json['email'] as String,
);
}
}
class ApiResponse<T> {
final int statusCode;
final T? data;
final String? message;
final bool success;
ApiResponse.success({required this.statusCode, required this.data})
: message = null,
success = true;
ApiResponse.error({required this.statusCode, required this.message})
: data = null,
success = false;
}
class ApiClient {
Future<ApiResponse<User>> getUser(String userId) async {
try {
final response = await http.get(
Uri.parse('https://api.example.com/users/$userId'),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
Map<String, dynamic> json = jsonDecode(response.body);
return ApiResponse.success(
statusCode: response.statusCode,
data: User.fromJson(json),
);
} else {
return ApiResponse.error(
statusCode: response.statusCode,
message: '获取用户失败',
);
}
} catch (e) {
return ApiResponse.error(
statusCode: -1,
message: '网络错误: $e',
);
}
}
}
9. 常见问题与解决方案
9.1 JSON解析失败
问题:jsonDecode抛出异常。
解决方案:
- 检查响应体是否为有效的JSON格式
- 在解析前检查
Content-Type是否为application/json - 使用try-catch捕获解析异常
9.2 数据类型不匹配
问题:JSON中的字段类型与期望不符。
解决方案:
- 使用
as操作符进行类型转换 - 使用
toString()转换后再解析 - 在
fromJson中添加类型检查
9.3 响应体为空
问题:response.body为空字符串。
解决方案:
- 检查状态码是否为204(No Content)
- 在解析前检查响应体是否为空
10. 总结
响应处理是网络编程的核心环节:
- 状态码判断:根据状态码判断请求是否成功
- 响应头解析:获取响应的元数据信息
- JSON解析:将响应体转换为Dart对象
- 二进制处理:处理图片、文件等二进制数据
- 响应封装:统一处理响应格式,提高代码复用性
掌握响应处理的方法,能够编写出更加健壮和可维护的网络请求代码。
参考资源
更多推荐



所有评论(0)