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

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>
    </>
  );
}

错误截图:
404错误截图

错误原因

  1. API路由结构不符合规范:Next.js App Router要求API路由文件放在app/api/[路由名]/route.js,原代码的路径/api/socket.io无法被框架正确识别。
  2. Socket.io服务器未绑定到WebSocket连接:原代码仅创建了Server实例,但未将其关联到请求的WebSocket连接上,导致无法处理客户端的连接请求。
  3. 客户端连接配置错误:io()默认使用/socket.io路径,与服务端实际路径不匹配;提前fetch API路由的操作多余,Socket.io客户端会自动完成握手流程。
  4. 未启用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>
  );
}

验证步骤

  1. 确保服务端代码文件路径为app/api/socket/route.js
  2. 安装依赖:npm install socket.io socket.io-client
  3. 启动开发服务器:npm run dev
  4. 访问http://localhost:3000,打开浏览器控制台查看连接日志
  5. 输入消息并发送,验证服务端是否收到消息,客户端是否收到回复

内容的提问来源于stack exchange,提问作者Rafi Sakib

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 16:52:13