Next.js 14.1.0 App目录Socket.io实现遇404错误求助
Next.js 14 App目录下Socket.io连接404问题解决方案
问题背景
在Next.js 14.1.0的App目录中搭建带房间功能的视频会议应用,尝试在API路由的GET方法中初始化Socket.io服务器并绑定到NextResponse,但客户端调用时持续出现GET 404 Not Found错误,需要实现同服务器下的客户端与服务端双向通信。
原服务端代码(app/api/socket.io/route.js):
import { NextResponse } from 'next/server'; import { Server } from 'socket.io'; export async function GET(req, res) { if (res.socketio) { console.log('socket already running'); return res; } res = new NextResponse(); const io = new Server({ path: '/api/socket.io', addTrailingSlash: false, }); io.on('connection', (socket) => { console.log('socket id: ', socket.id); socket.emit('welcome: ', socket.id); socket.on('message', (message) => { console.log(message); }); socket.on('disconnect', () => { console.log('socket is disconnecting'); }); }); res.socketio = io; return res; }
原客户端代码:
'use client'; import io from 'socket.io-client'; import { useState, useEffect } from 'react'; export default function Home() { const socket = io(); const [message, setMessage] = useState(''); const handleFormSubmit = (e) => { e.preventDefault(); socket.emit('message', message); }; useEffect(() => { const fetchIO = async () => await fetch('/api/socket.io'); fetchIO(); }, []); return ( <> <form onSubmit={handleFormSubmit}> <input className="border" type="text" name="message" value={message} onChange={(e) => setMessage(e.target.value)} /> <button type="submit">Submit</button> </form> </> ); }
错误截图:
错误原因
- API路由结构不符合规范:Next.js App Router要求API路由文件放在
app/api/[路由名]/route.js,原代码的路径/api/socket.io无法被框架正确识别。 - Socket.io服务器未绑定到WebSocket连接:原代码仅创建了Server实例,但未将其关联到请求的WebSocket连接上,导致无法处理客户端的连接请求。
- 客户端连接配置错误:
io()默认使用/socket.io路径,与服务端实际路径不匹配;提前fetch API路由的操作多余,Socket.io客户端会自动完成握手流程。 - 未启用Edge Runtime:Next.js处理WebSocket需要启用Edge Runtime,否则无法处理WebSocket升级请求。
修正后的代码
服务端代码(app/api/socket/route.js)
import { Server } from 'socket.io'; import { NextResponse } from 'next/server'; // 启用Edge Runtime以支持WebSocket export const runtime = 'edge'; export async function GET(req) { // 检查是否为WebSocket升级请求 if (!req.headers.get('upgrade') || req.headers.get('upgrade') !== 'websocket') { return new NextResponse('仅支持WebSocket连接', { status: 400 }); } // 获取WebSocket连接和响应对象 const { socket, response } = NextResponse.webSocket(); // 初始化Socket.io服务器并绑定到WebSocket连接 const io = new Server({ path: '/api/socket', }); io.attach(socket); // 监听连接事件 io.on('connection', (socket) => { console.log('客户端已连接:', socket.id); socket.emit('welcome', `欢迎连接,你的ID是:${socket.id}`); // 监听客户端发送的消息 socket.on('message', (message) => { console.log('收到客户端消息:', message); // 可选:给客户端返回消息确认 socket.emit('message-received', `服务器已收到:${message}`); }); // 监听断开连接事件 socket.on('disconnect', () => { console.log('客户端已断开连接:', socket.id); }); }); return response; }
客户端代码(app/page.js)
'use client'; import io from 'socket.io-client'; import { useState, useEffect } from 'react'; export default function Home() { const [socket, setSocket] = useState(null); const [message, setMessage] = useState(''); const [receivedMessage, setReceivedMessage] = useState(''); useEffect(() => { // 初始化Socket.io连接,指定服务端路径 const newSocket = io('/', { path: '/api/socket', transports: ['websocket'], // 强制使用WebSocket协议,避免轮询问题 }); // 监听连接成功事件 newSocket.on('connect', () => { console.log('已连接到服务器'); }); // 监听服务端发送的欢迎消息 newSocket.on('welcome', (msg) => { console.log(msg); }); // 监听服务端返回的消息确认 newSocket.on('message-received', (msg) => { setReceivedMessage(msg); }); // 监听断开连接事件 newSocket.on('disconnect', () => { console.log('已断开与服务器的连接'); }); setSocket(newSocket); // 组件卸载时断开连接 return () => { newSocket.disconnect(); }; }, []); const handleFormSubmit = (e) => { e.preventDefault(); if (socket && message.trim()) { socket.emit('message', message); setMessage(''); } }; return ( <div className="p-6 max-w-md mx-auto"> <h1 className="text-xl font-bold mb-4">Socket.io测试</h1> <form onSubmit={handleFormSubmit} className="flex gap-2 mb-4"> <input className="border border-gray-300 p-2 flex-1 rounded" type="text" name="message" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="输入消息..." /> <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" > 发送 </button> </form> {receivedMessage && ( <div className="p-2 bg-gray-100 rounded"> <p>服务器回复:{receivedMessage}</p> </div> )} </div> ); }
验证步骤
- 确保服务端代码文件路径为
app/api/socket/route.js - 安装依赖:
npm install socket.io socket.io-client - 启动开发服务器:
npm run dev - 访问
http://localhost:3000,打开浏览器控制台查看连接日志 - 输入消息并发送,验证服务端是否收到消息,客户端是否收到回复
内容的提问来源于stack exchange,提问作者Rafi Sakib
相关产品推荐
相关产品推荐

