Next.js App目录API路由中Pusher的socket_id与channel_name为何均为undefined?
问题分析
核心问题有两个:
- 客户端Pusher初始化时,异步获取Supabase session的方式错误,导致Authorization头无效,同时可能影响Pusher auth请求的正常发送。
- API路由错误地假设Pusher的auth请求是JSON格式,而Pusher-js默认发送的是
application/x-www-form-urlencoded类型的请求,导致无法解析出socket_id和channel_name。
解决方案
1. 修复客户端Pusher初始化(pusherClient.ts)
supabase.auth.getSession()是异步操作,不能直接在Pusher初始化时同步使用,需要用React的useEffect在组件挂载后异步获取session再初始化Pusher:
"use client"; import Pusher from "pusher-js"; import { createClient } from "@/utils/supabase/client"; import { useEffect, useState } from "react"; const supabase = createClient(); export default function usePusher() { const [pusher, setPusher] = useState<Pusher | null>(null); useEffect(() => { let client: Pusher | null = null; const initPusher = async () => { const { data: { session } } = await supabase.auth.getSession(); if (!session?.access_token) return; client = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY ?? '', { cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER ?? '', authEndpoint: "/api/pusher/auth", auth: { headers: { 'Authorization': `Bearer ${session.access_token}` } }, }); setPusher(client); }; initPusher(); return () => { client?.disconnect(); }; }, []); return pusher; }
这样可以确保只有拿到有效的session token后,才初始化Pusher,避免Authorization头携带无效值。
2. 修复API路由参数解析(app/api/pusher/auth/route.ts)
Pusher-js默认发送的auth请求是application/x-www-form-urlencoded格式,所以需要解析form数据而非JSON:
import { NextRequest, NextResponse } from "next/server"; import Pusher from "pusher"; import { createClient } from "@/utils/supabase/server"; const pusher = new Pusher({ appId: process.env.PUSHER_APP_ID!, key: process.env.PUSHER_KEY!, secret: process.env.PUSHER_SECRET!, cluster: process.env.PUSHER_CLUSTER!, useTLS: true, }); export async function POST(req: NextRequest) { console.log("Handler invoked"); try { // 解析form-urlencoded格式的请求体 const formData = await req.formData(); const socket_id = formData.get("socket_id") as string; const channel_name = formData.get("channel_name") as string; console.log("Socket ID:", socket_id); console.log("Channel Name:", channel_name); if (!socket_id || !channel_name) { return NextResponse.json({ error: "Missing socket_id or channel_name" }, { status: 400 }); } const authorizationHeader = req.headers.get('authorization'); const token = authorizationHeader?.split(' ')[1]; if (!token) { console.error("Missing auth token"); return NextResponse.json({ error: "Missing auth token" }, { status: 401 }); } console.log("Token:", token); const supabase = createClient(); const { data: { user }, error } = await supabase.auth.getUser(token); if (error || !user) { console.error("Invalid or expired token", error); return NextResponse.json({ error: "Invalid or expired token" }, { status: 401 }); } const userId = user.id; console.log("User ID:", userId); if (channel_name === `private-user-${userId}`) { const auth = pusher.authorizeChannel(socket_id, channel_name); return NextResponse.json(auth); } else { console.error("Forbidden: User not authorized for channel", channel_name); return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } } catch (error) { console.error("Internal Server Error", error); return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); } }
额外验证点
- 确保Pusher的环境变量在客户端(
NEXT_PUBLIC_前缀)和服务端都正确配置。 - 检查Supabase的session是否有效,确保用户已登录后再尝试连接Pusher私有频道。
- 可以在浏览器开发者工具的Network面板中查看Pusher发送的auth请求,确认
socket_id和channel_name是否在Form Data中,以及Authorization头是否正确。
内容的提问来源于stack exchange,提问作者vamp
相关产品推荐
相关产品推荐

