Flutter集成Agora时,云函数生成的RTC Token始终提示无效
问题排查与修复方案
我用以下云函数代码为Flutter应用生成Agora视频通话的RTC Token,Agora控制台生成的临时Token能正常使用,但云函数生成的Token却报错,已经确认AppID和AppCertificate是正确的。
import pkg from 'agora-access-token'; const { RtcTokenBuilder, RtcRole } = pkg; export default async ({ req, res, log, error }) => { if (req.method === 'POST') { let requestBody; if (typeof req.body === 'string') { requestBody = JSON.parse(req.body); } else { requestBody = req.body; } const { channelName, uid } = requestBody; const appId = process.env.APP_ID; const appCertificate = process.env.APP_CERTIFICATE; const role = RtcRole.PUBLISHER; log('Request body:', requestBody); log('Channel Name:', channelName); log('UID:', uid); log('App ID:', appId); log('App Certificate:', appCertificate); const expirationTimeInSeconds = 3600; const currentTimestamp = Math.floor(Date.now() / 1000); const privilegeExpiredTs = Math.floor(currentTimestamp + expirationTimeInSeconds); const token = RtcTokenBuilder.buildTokenWithUid( appId, appCertificate, channelName, uid, role, privilegeExpiredTs ); log('Generated Token:', token); return res.json({ token, uid: uid.toString() }); } else { res.status(405).json({ message: 'Method not allowed' }); } }
核心排查点与修复
1. UID类型不匹配
RtcTokenBuilder.buildTokenWithUid要求uid是整数类型,如果前端传入的uid是字符串格式(比如"12345"),直接传入会导致Token生成逻辑错误。
修复:
提取uid后转为整数,并处理非法值:
const { channelName, uid: rawUid } = requestBody; const uid = parseInt(rawUid, 10); const finalUid = isNaN(uid) ? 0 : uid; // 0表示Agora随机分配UID
2. 频道名大小写/格式问题
Agora频道名大小写敏感,且仅允许a-z、A-Z、0-9、-_!#$%&()+-<>.?@[]^_{}~这些字符。如果请求传入的频道名包含非法字符,或与前端连接时的频道名大小写不一致,会导致Token验证失败。
修复:
生成Token前标准化频道名:
const normalizedChannel = channelName.trim().toLowerCase();
3. 请求体解析异常
如果请求体格式不符合JSON规范,JSON.parse会抛出错误,导致后续逻辑中断。
修复:
添加解析异常捕获:
let requestBody; try { requestBody = typeof req.body === 'string' ? JSON.parse(req.body) : req.body; } catch (err) { error('请求体解析失败:', err); return res.status(400).json({ message: '无效的请求体格式' }); }
4. 环境变量与参数校验
虽然你确认过AppID和证书正确,但仍需确保云函数能完整读取环境变量,且请求参数不缺失。
修复:
添加参数与环境变量校验:
if (!channelName || rawUid === undefined) { return res.status(400).json({ message: '缺少channelName或uid参数' }); } if (!appId || !appCertificate) { error('环境变量APP_ID或APP_CERTIFICATE未配置'); return res.status(500).json({ message: '服务器配置错误' }); }
修复后的完整代码
import pkg from 'agora-access-token'; const { RtcTokenBuilder, RtcRole } = pkg; export default async ({ req, res, log, error }) => { if (req.method === 'POST') { let requestBody; try { requestBody = typeof req.body === 'string' ? JSON.parse(req.body) : req.body; } catch (parseErr) { error('请求体解析错误:', parseErr); return res.status(400).json({ message: '无效的请求体格式' }); } const { channelName, uid: rawUid } = requestBody; if (!channelName || rawUid === undefined) { return res.status(400).json({ message: '缺少channelName或uid参数' }); } const appId = process.env.APP_ID; const appCertificate = process.env.APP_CERTIFICATE; if (!appId || !appCertificate) { error('环境变量APP_ID或APP_CERTIFICATE未配置'); return res.status(500).json({ message: '服务器配置错误' }); } const role = RtcRole.PUBLISHER; const uid = parseInt(rawUid, 10); const finalUid = isNaN(uid) ? 0 : uid; const normalizedChannel = channelName.trim().toLowerCase(); const expirationTimeInSeconds = 3600; const currentTimestamp = Math.floor(Date.now() / 1000); const privilegeExpiredTs = currentTimestamp + expirationTimeInSeconds; const token = RtcTokenBuilder.buildTokenWithUid( appId, appCertificate, normalizedChannel, finalUid, role, privilegeExpiredTs ); log('生成Token成功:频道=', normalizedChannel, 'UID=', finalUid); return res.json({ token, uid: finalUid.toString() }); } else { res.status(405).json({ message: '不允许的请求方法' }); } }
内容的提问来源于stack exchange,提问作者MaryKor
相关产品推荐
相关产品推荐

