PAYATHON 2026

NodeJS API Testing 出现 EPROTO SSL 错误

数据阿明

结论

EPROTO 和 SSL routines:ssl3_get_record:wrong version number 表示客户端和服务端使用的协议不一致。

Express 通过 app.listen() 启动的是普通 HTTP 服务,而 Axios 请求的地址是:

https://localhost:3000

Axios 会发起 TLS/SSL 握手,但服务端只能处理 HTTP 请求,所以握手失败。把请求地址改为 http://localhost:3000 即可。

代码中还有两个问题需要修正:

  • Express 路由必须发送响应,否则请求会一直等待。
  • Axios 返回的数据位于 response.data,不能对响应调用 res.json()。

推荐修改

Express 服务

const express = require("express");
const mongoose = require("mongoose");

const PORT = process.env.PORT || 3000;
const Panch = require("./models/panchayat");

const app = express();

app.use(express.json());

mongoose.connect("mongodb://localhost/village", {
  useNewUrlParser: true,
  useUnifiedTopology: true
})
.then(() => {
  console.log("MongoDB connected");
})
.catch((error) => {
  console.error("MongoDB connection failed:", error);
});

app.post("/api/fetchPanch", async (req, res) => {
  try {
    const { id } = req.body;

    console.log(req.body);

    // 如果需要查询数据库:
    // const result = await Panch.findById(id);
    // return res.json(result);

    return res.status(200).json({
      success: true,
      id
    });
  } catch (error) {
    console.error(error);

    return res.status(500).json({
      success: false,
      message: error.message
    });
  }
});

app.listen(PORT, () => {
  console.log(`Server is up and running on http://localhost:${PORT}`);
});

日志中的地址也应使用 http://。日志不会改变服务器实际使用的协议,但写成 https:// 容易让调用方误以为服务已经启用 HTTPS。

Axios 测试代码

const axios = require("axios");

axios
  .post("http://localhost:3000/api/fetchPanch", {
    id: "5fd8eac45f2d508574c57fd5"
  })
  .then((response) => {
    console.log(response.data);
  })
  .catch((error) => {
    if (error.response) {
      console.error("API error:", error.response.status, error.response.data);
    } else {
      console.error("Request failed:", error.message);
    }
  });

也可以改用 async/await:

const axios = require("axios");

async function testApi() {
  try {
    const response = await axios.post(
      "http://localhost:3000/api/fetchPanch",
      {
        id: "5fd8eac45f2d508574c57fd5"
      }
    );

    console.log(response.data);
  } catch (error) {
    console.error(
      error.response
        ? {
            status: error.response.status,
            data: error.response.data
          }
        : error.message
    );
  }
}

testApi();

Axios 参数的正确位置

axios.post() 常用的调用形式是:

axios.post(url, data, config)

原代码传入了下面这个对象:

{
  headers: {
    "Access-Control-Allow-Origin": "*"
  },
  id: "5fd8eac45f2d508574c57fd5"
}

这个对象整体位于第二个参数,因此都会被当作请求体发送。其中的 headers 只是 JSON 数据,并不是真正的 HTTP 请求头。

如果确实需要设置请求头,应将配置放在第三个参数中:

axios.post(
  "http://localhost:3000/api/fetchPanch",
  {
    id: "5fd8eac45f2d508574c57fd5"
  },
  {
    headers: {
      "Content-Type": "application/json"
    }
  }
);

不过,Axios 发送普通对象时会自动设置合适的 JSON Content-Type,通常不需要手动指定。

Access-Control-Allow-Origin 不应由客户端发送

Access-Control-Allow-Origin 是服务端返回的 CORS 响应头,客户端不能靠发送这个请求头来解除跨域限制。

如果浏览器页面需要跨源调用这个 API,可以在 Express 中启用 CORS:

npm install cors
const cors = require("cors");

app.use(cors({
  origin: "*"
}));

直接在 Node.js 中运行 Axios 测试脚本通常不受浏览器同源策略限制,因此本地脚本测试一般不需要配置 CORS。

如果确实需要 HTTPS

要使用 https://localhost:3000,服务端也必须创建 HTTPS 服务器并配置证书。例如:

const fs = require("fs");
const https = require("https");

const options = {
  key: fs.readFileSync("./localhost-key.pem"),
  cert: fs.readFileSync("./localhost-cert.pem")
};

https.createServer(options, app).listen(3000, () => {
  console.log("Server is running on https://localhost:3000");
});

本地自签名证书还可能引发证书不受信任错误。开发环境可以把本地 CA 或证书加入系统信任。不建议关闭证书校验来绕过这个问题,生产环境更不能这样处理。

当前场景没有必须使用 HTTPS 的要求时,直接统一使用:

http://localhost:3000

这是最简单且正确的解决办法。

关于应用崩溃

TLS 错误发生在 Axios 请求端。原代码没有给 Promise 添加 .catch(),错误会成为未处理的 Promise rejection,并可能导致运行测试脚本的 Node.js 进程退出。

添加错误处理可以避免异常未被捕获:

axios
  .post("http://localhost:3000/api/fetchPanch", {
    id: "5fd8eac45f2d508574c57fd5"
  })
  .then((response) => {
    console.log(response.data);
  })
  .catch((error) => {
    console.error(error.message);
  });

如果 nodemon 运行的是测试文件,退出的就是测试进程。如果它运行的是服务端,还需要检查启动脚本和 MongoDB 连接中是否有未捕获的异常。无论是哪种情况,请求端和服务端都应显式处理 Promise rejection。

备注:内容仅供参考。