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

Node/Next.js中Pusher私有频道认证失败问题求助

问题:Pusher私有频道认证JSON解析错误

我正在开发应用的消息通知系统,已编写ChatContext提供者文件包裹整个应用,用户登录后会自动绑定Pusher。每个用户拥有专属私有频道private-unread-${session.user.id},需确保仅当前登录用户可连接该频道。

调用Next.js的POST接口/pusher/auth时,持续出现以下错误:

SyntaxError: Unexpected token 's', "socket_id="... is not valid JSON
at JSON.parse ()
at parseJSONFromBytes (node:internal/deps/undici/undici:5329:19)
at successSteps (node:internal/deps/undici/undici:5300:27)
at fullyReadBody (node:internal/deps/undici/undici:1447:9)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async specConsumeBody (node:internal/deps/undici/undici:5309:7)
at async POST (webpack-internal:///(rsc)/./app/api/pusher/auth/route.js:20:34)

尝试改用jsonp+GET方式后出现其他随机问题,希望尽量保留ajax方式。


客户端上下文代码

'use client'

import React, { createContext, useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import PusherClient from 'pusher-js';

const ChatContext = createContext();

const ChatProvider = ({ children }) => {
    const { data: session } = useSession();
    const [unreadChats, setUnreadChats] = useState([]);
    const [pusherConnected, setPusherConnected] = useState(false);

    useEffect(() => {
        if (session) {
            fetchUnreadMessages();
        }
    }, [session]);

    useEffect(() => {
        if (session && !pusherConnected) {
            const pusher = new PusherClient(process.env.NEXT_PUBLIC_PUSHER_APP_KEY, {
                cluster: 'us2',
                encrypted: true, 

                channelAuthorization: {
                    endpoint: '/api/pusher/auth',
                    params: {
                        channel_name: `private-unread-${session.user.id}`,
                    },
                },
            });

            const channel = pusher.subscribe(`private-unread-${session.user.id}`);
            channel.bind('message:new', function (data) {
                console.log(data);
                setMessages(currentState => [data.message, ...currentState])
            });

            setPusherConnected(true);

            return () => {
                channel.unbind('message:new')
            };
        }
    }, [session])

    return (
        <ChatContext.Provider value={{ unreadChats }}>
            {children}
        </ChatContext.Provider>
    );
};

export { ChatProvider, ChatContext };

认证路由代码

import PusherServer from 'pusher';

const pusherServer = new PusherServer({
    appId: process.env.PUSHER_APP_ID,
    key: process.env.NEXT_PUBLIC_PUSHER_APP_KEY, 
    secret: process.env.PUSHER_SECRET,
    cluster: 'us2',
    useTLS: true
});

export const POST = async (req) => {
    try {
        const { socket_id, channel_name } = await req.json();

        const token = **user's id pulled here from next auth**

        // Verify that the channel_name matches the user's ID (assuming channel_name is of the format `private-unread-<user_id>`)
        if (!channel_name.endsWith(token.sub)) {
            return new Response(JSON.stringify({ error: "Failed to auth pusher" }), { status: 403 });
        }

        const auth = pusherServer.authorizeChannel(socket_id, channel_name)

        return new Response(JSON.stringify(auth), {
            status: 200,
            headers: { 'Content-Type': 'application/json' }
        });
    } catch (error) {
        console.log(error);
        return new Response(JSON.stringify({ error: "Failed to auth pusher" }), { status: 403 });
    }
};

解决方案

错误核心原因:Pusher默认以application/x-www-form-urlencoded格式发送认证请求,但后端接口在尝试解析JSON格式请求体,导致JSON解析失败。

1. 后端修改:解析表单数据而非JSON

将Next.js POST路由中的req.json()替换为req.formData(),正确获取Pusher发送的表单数据:

export const POST = async (req) => {
    try {
        const formData = await req.formData();
        const socket_id = formData.get('socket_id');
        const channel_name = formData.get('channel_name');

        // 保留原有的用户ID获取逻辑
        const token = **从next auth获取用户ID的代码**

        if (!channel_name.endsWith(token.sub)) {
            return new Response(JSON.stringify({ error: "Pusher认证失败" }), { status: 403 });
        }

        const auth = pusherServer.authorizeChannel(socket_id, channel_name);

        return new Response(JSON.stringify(auth), {
            status: 200,
            headers: { 'Content-Type': 'application/json' }
        });
    } catch (error) {
        console.log(error);
        return new Response(JSON.stringify({ error: "Pusher认证失败" }), { status: 403 });
    }
};

2. 客户端代码修复(可选但推荐)

  • 修正未定义的setMessages,改为对应状态setUnreadChats
  • 优化清理函数,取消频道订阅并重置连接状态,避免内存泄漏
useEffect(() => {
    if (session && !pusherConnected) {
        const pusher = new PusherClient(process.env.NEXT_PUBLIC_PUSHER_APP_KEY, {
            cluster: 'us2',
            encrypted: true, 
            channelAuthorization: {
                endpoint: '/api/pusher/auth',
                params: {
                    channel_name: `private-unread-${session.user.id}`,
                },
            },
        });

        const channel = pusher.subscribe(`private-unread-${session.user.id}`);
        channel.bind('message:new', function (data) {
            console.log(data);
            setUnreadChats(currentState => [data.message, ...currentState])
        });

        setPusherConnected(true);

        return () => {
            channel.unbind('message:new');
            pusher.unsubscribe(`private-unread-${session.user.id}`);
            setPusherConnected(false);
        };
    }
}, [session])

3. 可选:客户端显式指定请求格式

如果需要强制指定请求格式,可在channelAuthorization配置中添加头部信息:

channelAuthorization: {
    endpoint: '/api/pusher/auth',
    params: {
        channel_name: `private-unread-${session.user.id}`,
    },
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    transport: 'ajax',
},

内容的提问来源于stack exchange,提问作者Sam Munroe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:12:32