请求头配置 - 鸿蒙FlutterHTTP元数据传递场景



概述
HTTP请求头是客户端发送给服务器的元数据信息,用于在请求中传递额外的参数和配置。请求头在HTTP通信中扮演着重要角色,涵盖了认证、内容类型、缓存控制等多个方面。
本章将详细介绍HTTP请求头的配置方法,包括常见请求头、认证头、内容类型头、自定义头以及请求头的封装技巧。
1. 常见HTTP请求头
HTTP协议定义了一系列标准请求头,用于传递各种元信息。以下是开发中常用的请求头:
| 请求头 | 说明 | 示例值 |
|---|---|---|
| Authorization | 身份认证信息 | Bearer eyJhbGciOiJIUzI1NiIs... |
| Content-Type | 请求体内容类型 | application/json |
| Accept | 可接受的响应类型 | application/json |
| User-Agent | 客户端标识 | Flutter/3.0.0 (Windows) |
| Referer | 请求来源页面 | https://example.com/page |
| Cache-Control | 缓存控制策略 | no-cache |
| X-Requested-With | 请求来源标识 | XMLHttpRequest |
| Accept-Language | 首选语言 | zh-CN,zh;q=0.9 |
| Origin | 请求来源域名 | https://example.com |
1.1 请求头的作用
请求头在HTTP通信中起到以下作用:
- 身份认证:通过Authorization头传递认证信息
- 内容协商:通过Accept头告诉服务器期望的响应格式
- 缓存控制:通过Cache-Control头控制缓存行为
- 客户端标识:通过User-Agent头标识客户端类型
- 跨域控制:通过Origin头支持CORS跨域请求
2. 认证请求头
认证是请求头最常用的场景之一,用于验证客户端身份。
2.1 Bearer Token认证
Bearer Token是现代API最常用的认证方式,通常用于JWT令牌传递。
import 'package:http/http.dart' as http;
Future<dynamic> fetchWithBearerToken(String token) async {
final response = await http.get(
Uri.parse('https://api.example.com/data'),
headers: <String, String>{
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
);
return jsonDecode(response.body);
}
Bearer Token的特点
- 无状态:服务器不需要存储会话信息
- 自包含:Token本身包含用户信息和权限
- 可扩展:可以携带自定义数据
- 安全:应通过HTTPS传输
2.2 Basic认证
Basic认证是一种简单的HTTP认证方式,将用户名和密码进行Base64编码后传递。
import 'dart:convert';
Future<dynamic> fetchWithBasicAuth(String username, String password) async {
String credentials = '$username:$password';
String encoded = base64Encode(utf8.encode(credentials));
final response = await http.get(
Uri.parse('https://api.example.com/data'),
headers: <String, String>{
'Authorization': 'Basic $encoded',
},
);
return jsonDecode(response.body);
}
Basic认证的特点
- 简单:实现方式简单
- 不安全:Base64编码可轻易解码,必须配合HTTPS使用
- 无状态:每次请求都需要传递认证信息
2.3 认证流程对比
Bearer Token认证流程:
1. 客户端发送用户名密码到/login端点
2. 服务器验证后返回JWT Token
3. 客户端在后续请求的Authorization头中携带Token
4. 服务器验证Token有效性
Basic认证流程:
1. 客户端在每次请求的Authorization头中携带Base64编码的用户名密码
2. 服务器解码并验证用户名密码
3. Content-Type配置
Content-Type头用于指定请求体的格式,服务器根据此头解析请求数据。
3.1 JSON格式
现代RESTful API最常用的格式,需要设置Content-Type为application/json。
headers: {
'Content-Type': 'application/json; charset=UTF-8',
}
3.2 表单格式
传统表单提交使用application/x-www-form-urlencoded格式。
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
注意:使用http包提交表单时,直接传递Map<String, String>作为body,http包会自动设置正确的Content-Type。
http.post(
Uri.parse('https://api.example.com/login'),
body: {'username': 'user', 'password': 'pass'},
);
3.3 文件上传格式
文件上传使用multipart/form-data格式,需要使用MultipartRequest。
import 'package:http/http.dart' as http;
Future<void> uploadFile(String filePath) async {
var request = http.MultipartRequest(
'POST',
Uri.parse('https://api.example.com/upload'),
);
request.files.add(await http.MultipartFile.fromPath('file', filePath));
var response = await request.send();
}
3.4 其他常见格式
| Content-Type | 用途 |
|---|---|
text/plain |
纯文本数据 |
text/html |
HTML内容 |
image/jpeg |
JPEG图片 |
application/xml |
XML数据 |
4. 自定义请求头
除了标准请求头,开发者还可以定义自定义请求头,通常以X-前缀开头。
4.1 常用自定义请求头
Future<dynamic> fetchWithCustomHeaders() async {
final response = await http.get(
Uri.parse('https://api.example.com/data'),
headers: <String, String>{
'Authorization': 'Bearer your_token',
'X-API-Version': 'v2',
'X-Client-Id': 'flutter_app',
'X-Request-Id': 'unique-request-id',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
},
);
return jsonDecode(response.body);
}
4.2 自定义请求头的用途
- API版本控制:通过
X-API-Version指定API版本 - 客户端标识:通过
X-Client-Id标识客户端类型 - 请求追踪:通过
X-Request-Id追踪请求链路 - 语言偏好:通过
Accept-Language传递语言偏好 - 业务参数:通过自定义头传递业务特定参数
4.3 自定义请求头命名规范
- 使用
X-前缀标识非标准头部(RFC 6648已废弃此建议,但仍广泛使用) - 头部名称使用短横线分隔(kebab-case)
- 避免使用下划线和大写字母
- 保持头部名称简洁明了
5. 请求头封装
在实际项目中,请求头配置往往重复出现,因此需要进行封装。
5.1 封装请求头工具类
import 'dart:convert';
class ApiHeaders {
static Map<String, String> get defaultHeaders {
return {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'FlutterApp/1.0.0',
};
}
static Map<String, String> withToken(String token) {
return {
...defaultHeaders,
'Authorization': 'Bearer $token',
};
}
static Map<String, String> withBasicAuth(String username, String password) {
String credentials = '$username:$password';
String encoded = base64Encode(utf8.encode(credentials));
return {
...defaultHeaders,
'Authorization': 'Basic $encoded',
};
}
static Map<String, String> withCustom(Map<String, String> custom) {
return {
...defaultHeaders,
...custom,
};
}
}
5.2 使用封装的请求头
// 使用默认请求头
http.get(uri, headers: ApiHeaders.defaultHeaders);
// 使用带Token的请求头
http.get(uri, headers: ApiHeaders.withToken(token));
// 使用带自定义头的请求头
http.get(uri, headers: ApiHeaders.withCustom({'X-Client-Id': 'my_app'}));
5.3 结合拦截器封装
在复杂项目中,可以结合拦截器实现请求头的统一处理:
class AuthInterceptor extends http.BaseClient {
final http.Client _client = http.Client();
final String _token;
AuthInterceptor(this._token);
Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers['Authorization'] = 'Bearer $_token';
request.headers['Content-Type'] = 'application/json';
return _client.send(request);
}
}
// 使用
final client = AuthInterceptor('my_token');
final response = await client.get(Uri.parse('https://api.example.com/data'));
6. 请求头最佳实践
6.1 安全方面
- 使用HTTPS:所有包含认证信息的请求必须通过HTTPS传输
- 避免敏感信息:不要在请求头中传递密码等敏感信息(除认证外)
- Token刷新:实现Token过期自动刷新机制
- Token存储:使用安全方式存储Token(如Flutter Secure Storage)
6.2 性能方面
- 减少请求头大小:避免添加不必要的自定义头
- 合理设置缓存:通过Cache-Control头减少重复请求
- 启用压缩:通过Accept-Encoding头启用响应压缩
6.3 兼容性方面
- 标准化命名:使用标准请求头名称,避免自定义头冲突
- 版本控制:通过X-API-Version头支持API版本演进
- 优雅降级:确保老版本客户端能正常工作
6.4 调试方面
- 请求追踪:添加X-Request-Id便于日志追踪
- 客户端标识:添加X-Client-Id便于问题定位
- 详细日志:记录请求头信息便于调试
7. 实践示例:完整的请求头配置
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiClient {
final String baseUrl;
String? _token;
ApiClient({required this.baseUrl});
void setToken(String token) {
_token = token;
}
Map<String, String> _buildHeaders({bool requireAuth = true}) {
Map<String, String> headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'User-Agent': 'FlutterApp/1.0.0',
'X-Request-Id': '${DateTime.now().millisecondsSinceEpoch}',
};
if (requireAuth && _token != null) {
headers['Authorization'] = 'Bearer $_token!';
}
return headers;
}
Future<dynamic> get(String path, {Map<String, String>? query}) async {
Uri uri = Uri.parse('$baseUrl$path');
if (query != null) {
uri = uri.replace(queryParameters: query);
}
final response = await http.get(uri, headers: _buildHeaders());
return jsonDecode(response.body);
}
Future<dynamic> post(String path, Map<String, dynamic> body) async {
final response = await http.post(
Uri.parse('$baseUrl$path'),
headers: _buildHeaders(),
body: jsonEncode(body),
);
return jsonDecode(response.body);
}
}
8. 常见问题与解决方案
8.1 请求头不生效
问题:设置的请求头没有被发送到服务器。
解决方案:
- 检查http包版本,确保使用最新版本
- 确认请求头名称正确(区分大小写)
- 通过抓包工具(如Charles、Fiddler)检查实际发送的请求
8.2 跨域请求被拒绝
问题:跨域请求时,自定义请求头导致预检请求失败。
解决方案:
- 确保服务器配置了正确的CORS策略
- 避免在简单请求中使用自定义头
- 对于复杂请求,确保服务器响应了OPTIONS预检请求
8.3 Token过期处理
问题:Token过期后请求失败。
解决方案:
- 实现Token刷新机制
- 在拦截器中统一处理401状态码
- 使用Refresh Token获取新的Access Token
9. 总结
请求头是HTTP通信的重要组成部分,正确配置请求头对于确保API调用的安全性、可靠性和兼容性至关重要。
- 认证头:Bearer Token是现代API的首选认证方式
- 内容类型头:根据请求体格式正确设置Content-Type
- 自定义头:使用X-前缀标识自定义头,用于传递业务参数
- 封装技巧:将请求头封装为工具类,提高代码复用性
- 安全意识:始终通过HTTPS传输敏感信息
掌握请求头的配置方法,是成为一名优秀的Flutter开发者的必备技能。
参考资源
更多推荐



所有评论(0)