如何使用pusher-js订阅并接收服务器到用户频道的数据?
问题背景
我在Vercel上部署了Next.js应用,想通过Pusher实现实时消息功能,让每个用户通过私有安全频道接收消息。从日志看流程没问题,但就是没法成功订阅频道并接收事件消息,试了各种方法都没解决。
服务端代码
/pusher/user-auth/index.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) { const pusherService = container.resolve(PusherService); const manageToken = container.resolve(ManageToken); const loggingService = container.resolve(LoggingService); loggingService.info('PUSHER /api/pusher/user-auth - Authenticate user'); const socketId = req.body.socket_id as string; const token = req.headers['x-csrf-token'] as string; const currentUser = await manageToken.verifyUserToken(token); const authResponse = await pusherService.authenticateUser(socketId, { id: currentUser.id, user_info: { lastName: currentUser.lastName, firstName: currentUser.firstName, email: currentUser.email, }, }); res.status(200).json(authResponse); return; }
/pusher/auth/index.ts
export default async function handler(req: NextApiRequest, res: NextApiResponse) { const pusherService = container.resolve(PusherService); const loggingService = container.resolve(LoggingService); loggingService.info('PUSHER /api/pusher/auth - Authorize channel.'); const socketId = req.body.socket_id as string; const channel = req.body.channel_name; const authResponse = pusherService.authorizeChannel(socketId, channel); res.status(200).json(authResponse); return; }
/pusher-service.ts
public async sendToUser(userId: string, event: string, data: any) { this.loggingService.info('Send event to user via Pusher.'); try { return this.pusher.sendToUser(userId, 'generated', {message: 'hi'}); } catch (e) { this.loggingService.error(e); } }
客户端代码
export const subscribe = async (token) => { log.info("Subscribe pusher channel."); Pusher.logToConsole = true; const pusher = new Pusher(process.env.REACT_APP_PUSHER_KEY, { cluster: "eu", channelAuthorization: { endpoint: process.env.REACT_APP_BLOOO_API_URL + "/pusher/auth" }, userAuthentication: { endpoint: process.env.REACT_APP_BLOOO_API_URL + "/pusher/user-auth", headers: { "X-CSRF-Token": "Bearer " + token } } }); pusher.signin(); // 订阅示例(无法正常工作) // const channel = pusher.subscribe("#server-to-user-444") // channel.bind("generated", (data) => { // console.log(data); // }); };
客户端日志
Pusher : : ["State changed","initialized -> connecting"] logger.ts:19 Pusher : : ["Connecting",{"transport":"ws","url":"wss://ws-eu.pusher.com:443/app/xxx?protocol=7&client=js&version=8.1.0&flash=false"}] logger.ts:19 Pusher : : ["State changed","connecting -> connected with new socket ID 123.123"] logger.ts:19 Pusher : : ["Event sent",{"event":"pusher:signin","data":{"auth":"123:123","user_data":"{\"id\":\"444\",\"user_info\":{\"lastName\":\"NAME\",\"firstName\":\"NAME\",\"email\":\"MAIL\"}}"}}] logger.ts:19 Pusher : : ["Event recd",{"event":"pusher:signin_success","data":{"user_data":"{\"id\":\"444\",\"user_info\":{\"lastName\":\"NAME\",\"firstName\":\"NAME\",\"email\":\"MAIL\"}}"}}] logger.ts:19 Pusher : : ["Event sent",{"event":"pusher:subscribe","data":{"auth":"","channel":"#server-to-user-444"}}] logger.ts:19 Pusher : : ["Event recd",{"event":"pusher_internal:subscription_succeeded","channel":"#server-to-user-444","data":{}}] logger.ts:19 Pusher : : ["No callbacks on #server-to-user-444 for pusher:subscription_succeeded"] logger.ts:19 Pusher : : ["Event recd",{"event":"generated","channel":"#server-to-user-444","data":{"message":"hi"}}] logger.ts:19 Pusher : : ["No callbacks on user for generated"] logger.ts:19 Pusher : : ["No callbacks on #server-to-user-444 for generated"]
问题排查与解决方案
从日志能看到,generated事件已经成功推送到#server-to-user-444频道,但客户端没触发回调,核心原因和修复方案如下:
1. 核心问题
- 订阅代码未执行:客户端里的订阅和绑定代码是注释状态,根本没运行。
- 实例作用域失效:
pusher是函数内局部变量,函数执行完就被销毁,后续事件无法触发回调。 - 订阅时机错误:
pusher.signin()是异步操作,直接在后面写订阅代码可能还没完成认证就执行了。
2. 修正后的客户端代码
// 把pusher实例放在全局作用域或组件状态(比如React的useRef)中,避免被销毁 let pusherInstance = null; export const subscribe = async (token) => { log.info("Subscribe pusher channel."); Pusher.logToConsole = true; pusherInstance = new Pusher(process.env.REACT_APP_PUSHER_KEY, { cluster: "eu", channelAuthorization: { endpoint: process.env.REACT_APP_BLOOO_API_URL + "/pusher/auth" }, userAuthentication: { endpoint: process.env.REACT_APP_BLOOO_API_URL + "/pusher/user-auth", headers: { "X-CSRF-Token": "Bearer " + token } } }); // 监听signin成功事件,确保认证完成后再订阅频道 pusherInstance.bind('pusher:signin_success', (userData) => { const userId = userData.id; const channel = pusherInstance.subscribe(`#server-to-user-${userId}`); // 绑定事件回调 channel.bind("generated", (data) => { console.log("收到实时消息:", data); }); }); pusherInstance.signin(); };
3. 额外检查点
- 频道权限验证:服务端
/pusher/auth接口要检查频道名称中的用户ID和当前认证用户ID是否匹配,防止无权限订阅。 - 服务端发送逻辑:
sendToUser方法里固定写死了事件名'generated',如果调用时传入的event参数没用上,要确认是否符合需求。 - Token解析:客户端传递的
X-CSRF-Token带Bearer前缀,服务端verifyUserToken方法要先去掉前缀再验证。
内容的提问来源于stack exchange,提问作者laprof
相关产品推荐
相关产品推荐

