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。
备注:内容仅供参考。
