You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js App目录API路由中Pusher的socket_id与channel_name为何均为undefined?

问题分析

核心问题有两个:

  1. 客户端Pusher初始化时,异步获取Supabase session的方式错误,导致Authorization头无效,同时可能影响Pusher auth请求的正常发送。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 21:14:49