7.2 KiB
7.2 KiB
UTS AkReq HEAD 请求支持说明
概述
为了正确支持 Supabase 的 head: true 选项,我们对 ak-req.uts 进行了增强,使其能够正确处理 HEAD 请求的特殊响应格式。
HEAD 请求的特点
HTTP 标准
- HEAD 请求与 GET 请求相同,但只返回响应头,不返回响应体
- 适用于只需要元数据而不需要实际内容的场景
- 用于检查资源状态、获取缓存信息、计算文件大小等
Supabase 中的应用
{ head: true }选项会使用 HEAD 请求- 响应体为空,但
Content-Rangeheader 包含 count 信息 - 极大减少网络传输量
AkReq 的增强实现
修改前的问题
// 原始实现:不区分 HEAD 和 GET
success: (res) => {
// 总是尝试解析 res.data,即使是 HEAD 请求
let data: UTSJSONObject | Array<any> | null;
if (typeof res.data == 'string') {
// ... 解析逻辑
}
}
修改后的正确处理
success: (res) => {
// HEAD 请求特殊处理:没有响应体,只有 headers
if (options.method === 'HEAD') {
const result = {
status: res.statusCode,
data: null, // HEAD 请求没有数据
headers: res.header as UTSJSONObject,
error: null
} as AkReqResponse<any>
resolve(result);
return;
}
// 其他请求正常解析 data
// ...
}
完整的数据流
1. 发起 HEAD 请求
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', userId)
.execute()
2. AkSupa 构建请求
// aksupa.uts
let httpMethod = 'GET';
if (options != null && options.head == true) {
httpMethod = 'HEAD'; // 使用 HEAD 方法
}
let reqOptions: AkReqOptions = {
url,
method: httpMethod, // 'HEAD'
headers: {
'Prefer': 'count=exact,return=minimal'
}
}
3. AkReq 处理响应
// ak-req.uts
if (options.method === 'HEAD') {
return {
status: 200,
data: null, // HEAD 请求没有响应体
headers: { // 重要信息在 headers 中
'content-range': '0-0/42', // count 信息
'content-type': 'application/json'
},
error: null
}
}
4. AkSupa 解析 count
// aksupa.uts
// 从 content-range header 解析 count
let contentRange = res.headers?.get('content-range')
if (contentRange != null) {
const match = /\/(\d+)$/.exec(contentRange);
if (match != null) {
total = parseInt(match[1] ?? "0"); // 提取 count
}
}
// HEAD 模式返回 count
if (this._options.head == true) {
return {
data: null,
count: total, // 解析出的 count
total,
// ...
};
}
5. 应用层使用
// dashboard.uvue
const result = await supa.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.execute()
const count = result.total // 42
网络请求对比
GET 请求(大量数据传输)
Request:
GET /rest/v1/ak_assignments?select=*&count=exact&teacher_id=eq.123
Response:
HTTP/1.1 200 OK
Content-Range: 0-9/42
Content-Type: application/json
[
{"id": 1, "title": "作业1", "description": "...", ...},
{"id": 2, "title": "作业2", "description": "...", ...},
// ... 更多数据
]
HEAD 请求(最小数据传输)
Request:
HEAD /rest/v1/ak_assignments?select=*&count=exact&teacher_id=eq.123
Response:
HTTP/1.1 200 OK
Content-Range: 0-0/42
Content-Type: application/json
(无响应体)
性能优势
传输量对比
| 请求类型 | 响应体大小 | 网络传输 | 解析开销 |
|---|---|---|---|
| GET | ~50KB | 大 | 高 |
| HEAD | 0KB | 小 | 极低 |
实际场景
// 统计查询场景
const stats = await Promise.all([
supa.from('assignments').select('*', { count: 'exact', head: true }).execute(),
supa.from('students').select('*', { count: 'exact', head: true }).execute(),
supa.from('courses').select('*', { count: 'exact', head: true }).execute()
])
// 如果用 GET:可能传输几百KB数据
// 使用 HEAD:只传输几KB headers
错误处理
HEAD 请求可能的错误
const safeHeadRequest = async (queryBuilder: any) => {
try {
const result = await queryBuilder.execute()
// HEAD 请求成功但没有 count 信息
if (result.data === null && result.total === 0) {
console.warn('HEAD 请求可能没有正确解析 count')
}
return result.total ?? 0
} catch (error) {
console.error('HEAD 请求失败:', error)
return 0
}
}
调试 HEAD 请求
const debugHeadRequest = async () => {
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.execute()
console.log('HEAD 请求结果:')
console.log('- Status:', result.status)
console.log('- Data:', result.data) // 应该为 null
console.log('- Total:', result.total) // 应该有数值
console.log('- Headers:', result.headers) // 包含 content-range
return result
}
最佳实践
1. 统计查询使用 HEAD
// ✅ 推荐:统计查询使用 HEAD
const getAssignmentCount = async (teacherId: string) => {
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact', head: true })
.eq('teacher_id', teacherId)
.execute()
return result.total ?? 0
}
2. 数据查询使用 GET
// ✅ 推荐:需要数据时使用 GET
const getAssignmentList = async (teacherId: string) => {
const result = await supa
.from('ak_assignments')
.select('*', { count: 'exact' }) // 不使用 head
.eq('teacher_id', teacherId)
.limit(10)
.execute()
return {
data: result.data,
total: result.total
}
}
3. 混合场景
// 先获取总数,再按需获取数据
const loadDashboard = async (teacherId: string) => {
// 1. 快速获取统计(HEAD)
const [totalCount, completedCount] = await Promise.all([
supa.from('ak_assignments').select('*', { count: 'exact', head: true }).eq('teacher_id', teacherId).execute(),
supa.from('ak_assignments').select('*', { count: 'exact', head: true }).eq('teacher_id', teacherId).eq('status', 'completed').execute()
])
// 2. 根据需要获取具体数据(GET)
const recentData = await supa
.from('ak_assignments')
.select('*')
.eq('teacher_id', teacherId)
.order('created_at', { ascending: false })
.limit(5)
.execute()
return {
stats: {
total: totalCount.total,
completed: completedCount.total
},
recent: recentData.data
}
}
总结
通过增强 ak-req.uts 对 HEAD 请求的支持:
- 正确处理:HEAD 请求不解析响应体,直接返回
data: null - 性能优化:大幅减少网络传输量
- 符合标准:遵循 HTTP HEAD 请求规范
- 无缝集成:与现有 Supabase count 功能完美配合
这使得 UTS 应用能够高效地进行数据统计查询,特别适合仪表板、统计页面等场景。