forked from 77media/video-flow
5.2 KiB
5.2 KiB
Video-Flow API 使用指南(方案一:保持现状,统一规范与示例)
目标:在不改动现有代码结构与签名的前提下,提供清晰的一致性用法、错误处理约定与多场景接入示例。
目录结构与职责
constants.ts:基础配置(BASE_URL从NEXT_PUBLIC_BASE_URL注入)request.ts:Axios 实例与拦截器、通用get/post/put/del、stream(SSE风格下载进度)、downloadStream、streamJsonPosterrorHandle.ts:错误码映射与统一提示、特殊码处理(如 401 跳转登录、402 不弹提示)common.ts:通用类型与与上传相关的工具(获取七牛 Token、上传)resources.ts:示例资源接口封装(基于post)- 其他业务模块(如
video_flow.ts、export-adapter.ts等)各自封装具体接口/逻辑
统一调用规范
- 使用
request.ts提供的get/post/put/del包装函数发起请求,返回后端响应体(已通过响应拦截器做业务码检查)。 - 业务成功码:
code === 0或code === 202(长任务/排队等需要前端自行处理状态)。若非成功码,拦截器会调用errorHandle并Promise.reject。 - 认证:前端从
localStorage.token注入Authorization: Bearer <token>,请确保登录流程写入token。 - 基础地址:通过环境变量
NEXT_PUBLIC_BASE_URL注入,构建前需设置。
错误处理约定
errorHandle(code, message?):统一弹出错误(402 除外),并处理特殊码:401:清除localStorage.token并跳转/login402:由request.ts响应拦截器在收到后端402且带detail时触发通知(积分不足),并拒绝请求
- 网络/解析类错误:
handleRequestError会降级为通用错误提示并抛出异常
常用请求示例
import { get, post, put, del } from '@/api/request';
type User = { id: number; name: string };
type ApiResponse<T> = { code: number; successful: boolean; message: string; data: T };
// GET
const user = await get<ApiResponse<User>>('/user/detail?id=1');
// POST
const created = await post<ApiResponse<User>>('/user/create', { name: 'Evan' });
// PUT
await put<ApiResponse<null>>('/user/update', { id: 1, name: 'Kent' });
// DELETE
await del<ApiResponse<null>>('/user/remove?id=1');
流式与下载(SSE/文件)
- SSE/渐进消息(基于 Axios 下载进度事件解析
data: {}行)
import { stream } from '@/api/request';
await stream<{ type: string; message?: string; progress?: number }>({
url: '/api/video-flow/export/ai-clips',
method: 'POST',
data: { /* your body */ },
onMessage: (evt) => {
if (evt.type === 'progress') {
// 更新进度条
}
},
onError: (err) => { /* 兜底错误处理 */ },
onComplete: () => { /* 完成回调 */ }
});
- 文本行分隔的 JSON 流(
fetch+ReadableStream,每行一个 JSON)
import { streamJsonPost } from '@/api/request';
await streamJsonPost('/sse/json', { /* body */ }, (json) => {
// 每次解析到一行 JSON
});
- 文件下载(Blob)
import { downloadStream } from '@/api/request';
await downloadStream('/download/file', 'result.mp4');
多场景接入指南
浏览器前端(React/Next.js CSR)
- 直接使用
get/post/put/del;确保登录后将token写入localStorage - 环境变量:在
.env.local配置NEXT_PUBLIC_BASE_URL - 错误提示:由
errorHandle统一处理;402 会展示积分不足通知
Next.js Route Handler(服务端 API)
- 如需在 Route Handler 内调用后端接口:建议使用服务端
fetch并自行附加服务端凭证;避免直接依赖localStorage - 若需要转发到现有后端并复用通知/事件流格式,可参考
api/export-adapter.ts的 SSE 写法
Next.js Server Components/SSR
- 服务端不具备
localStorage,如需鉴权请改为从 Cookie/Headers 传递 token,并在转发时设置Authorization - 服务器端可直接使用
fetch(BASE_URL + path, { headers })
Node/Serverless(Vercel/Cloudflare)
- 同 SSR 原则:不使用
localStorage;从密钥存储中读取后以请求头传递 - SSE 转发时,按
text/event-stream写入ReadableStream并传递data: <json>\n\n
最佳实践
- 统一通过
request.ts发起请求,不绕过拦截器;对长任务/排队返回202时在页面层处理轮询或 SSE - 上传流程:使用
common.ts/getUploadToken获取 token 与 domain,再使用uploadToQiniu上传,监听进度 - 将后端的业务错误码保持为服务端规范,在前端只做展示与关键码分支(401/402)
常见问题(FAQ)
- Q: 为什么拿不到
localStorage中的 token?- A: 服务端渲染或 Route Handler 中无
localStorage,需通过 Cookie/Headers 传递。
- A: 服务端渲染或 Route Handler 中无
- Q: 返回
code !== 0/202时如何自定义处理?- A: 可在业务层
try/catch捕获post/get的拒绝 Promise,并根据error.message或后端data.message做分支。
- A: 可在业务层
- Q: SSE 收不到事件?
- A: 确认后端使用
text/event-stream,每条消息以data: <json>\n\n输出;前端在onDownloadProgress中按行解析。
- A: 确认后端使用