在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

概述

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. 总结

响应处理是网络编程的核心环节:

  1. 状态码判断:根据状态码判断请求是否成功
  2. 响应头解析:获取响应的元数据信息
  3. JSON解析:将响应体转换为Dart对象
  4. 二进制处理:处理图片、文件等二进制数据
  5. 响应封装:统一处理响应格式,提高代码复用性

掌握响应处理的方法,能够编写出更加健壮和可维护的网络请求代码。


参考资源

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐