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

Express请求挂起排查:我的Node.js服务代码哪里出错了?

Node.js Express API 请求无响应问题排查

我基于Express编写了封装文件系统方法的Node.js API服务,使用Thunder Client或Postman发送请求时,请求始终处于处理状态无法完成。控制台日志显示请求流程已走完(输出finished api),但客户端接收不到响应。使用系统为macOS 14 Sonoma,请帮忙排查代码问题。

代码实现

export default class Server {
  private app: Express;
  public port: number = 3000;

  constructor() {
    if (!isNode) {
      throw new Error("The server must be initialized on the server side");
    }

    this.app = express();
  }

  public init() {
    this.port = this.port++;
    if (this.port > 3050) {
      console.warn(
        "The server has already tried to start on 50 different ports. It is reccomended to open a port in the range of 3000 to 3050 for the best performance."
      );
    }

    if (this.port > 3100) {
      throw new Error(
        "Attempted to start the server on 100 different ports. None were open, aborting."
      );
    }

    try {
      this.app.listen(this.port++);
      this.api();
    } catch (err: any) {
      if (err.code === "EADDRINUSE") {
        this.init();
      }

      throw err;
    }
  }

    private async api() {
    console.log("api called");

    const catMethods = {
      GET: [
        "access",
        "lstat",
        "stat",
        "statfs",
        "readFile",
        "readdir",
        "readlink",
        "realpath",
        "watch",
        "opendir",
        "glob",
        "constants"
      ],
      PUT: [
        "chmod",
        "chown",
        "lchmod",
        "lchown",
        "utimes",
        "lutimes",
        "truncate",
        "writeFile",
        "appendFile"
      ],
      POST: ["mkdir", "mkdtemp", "open", "symlink", "link", "copyFile", "cp"],
      DELETE: ["rmdir", "unlink", "rename"]
    };

    const fsMethod = (
      // @ts-ignore
      // Just copying types from express
      req: Request<{}, any, any, QueryString.ParsedQs, Record<string, any>>,
      // @ts-ignore
      // Just copying types from express
      res: Response<any, Record<string, any>, number>,
      catMethod: string[]
    ) => {
      console.log("fs method called");
      if (
        typeof req.headers.method !== "string" ||
        typeof req.headers.args !== "string"
      ) {
        return res
          .status(422)
          .json({success: false, error: "Invalid header encoding not found"});
      }

      const method = req.headers.method;
      if (!method) {
        return res
          .status(422)
          .json({success: false, error: "Method not found"});
      }

      console.log("obtained headers");

      const args = JSON.parse(req.headers.args || "[]");
      let matchedMethod = false;

      for (let i = 0; i < catMethod.length; i++) {
        if (method === catMethod[i]) {
          matchedMethod = true;
        }
      }

      if (!matchedMethod) {
        return res
          .status(405)
          .json({success: false, error: "Method not allowed"});
      }

      console.log("found match");

      try {
        this.method(method, args);
        console.log("ran method");
      } catch (err: any) {
        return res.status(500).json({success: false, error: err});
      }

      console.log("finished api");
    };

    this.app.get("/fs", async (req, res) => {
      console.log("get");
      fsMethod(req, res, catMethods.GET);
    });

    this.app.post("/fs", async (req, res) => {
      fsMethod(req, res, catMethods.POST);
    });

    this.app.put("/fs", async (req, res) => {
      fsMethod(req, res, catMethods.PUT);
    });

    this.app.delete("/fs", async (req, res) => {
      fsMethod(req, res, catMethods.DELETE);
    });
  }
}

const server = new Server();
server.init();
console.log("server started");

控制台日志

# 请求前
api called
server started

# 请求后
get
fs method called
obtained headers
found match
ran method
finished api 

问题分析与修复方案

1. 核心问题:未向客户端发送响应

Express请求必须通过res.send()、res.json()等方法明确结束,否则请求会一直处于挂起状态。你的fsMethod函数在try块执行成功后,没有任何返回响应的逻辑,导致客户端一直等待。

修复:
在try块中执行完this.method后,添加成功响应:

try {
  const result = await this.method(method, args); // 如果是异步方法要加await
  console.log("ran method");
  return res.status(200).json({success: true, data: result});
} catch (err: any) {
  return res.status(500).json({success: false, error: err.message});
}

2. 异步方法未处理等待

如果this.method内部调用了Node.js的异步文件系统API(比如fs.promises.readFile),当前代码同步调用会导致console.log("ran method")提前执行,但实际操作还未完成,同时也无法获取返回结果。

修复:
确保this.method是异步函数,在调用时添加await,同时将fsMethod改为异步函数:

const fsMethod = async (
  req: Request<{}, any, any, QueryString.ParsedQs, Record<string, any>>,
  res: Response<any, Record<string, any>, number>,
  catMethod: string[]
) => {
  // ... 原有逻辑
  try {
    const result = await this.method(method, args);
    console.log("ran method");
    return res.status(200).json({success: true, data: result});
  } catch (err: any) {
    return res.status(500).json({success: false, error: err.message});
  }
}

3. 端口初始化逻辑错误

this.port = this.port++是无效的赋值,后置自增运算符会先返回this.port的原值,再自增,所以这行代码相当于没有改变this.port的值。此外this.app.listen(this.port++)再次自增,导致实际监听的端口和预期不符。

修复:
修改端口自增逻辑:

public init() {
  // 先检查当前端口是否超出范围
  if (this.port > 3050) {
    console.warn("The server has already tried to start on 50 different ports. It is reccomended to open a port in the range of 3000 to 3050 for the best performance.");
  }

  if (this.port > 3100) {
    throw new Error("Attempted to start the server on 100 different ports. None were open, aborting.");
  }

  const server = this.app.listen(this.port, (err?: any) => {
    if (err) {
      if ((err as any).code === "EADDRINUSE") {
        this.port++;
        this.init();
      } else {
        throw err;
      }
    } else {
      console.log(`Server running on port ${this.port}`);
      this.api();
    }
  });
}

4. listen错误处理无效

this.app.listen()是异步方法,错误无法通过try/catch捕获,需要通过回调函数或监听server对象的error事件处理端口占用问题。

修复:
上面的端口初始化修复已经包含了正确的错误处理逻辑,通过listen的回调函数处理端口占用,自动重试下一个端口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:14:52