在实时推文流媒体 socket.io 应用程序上将它们发送到客户端后无法设置 headers
Cannot set headers after they are sent to the client on real time tweet streamer socket.io app
我正在 node.js 和 react.js 上创建实时 Twitter 流媒体应用程序,并且在 npm 启动时遇到此错误。我已经修复了 package.json 文件,该应用程序确实在本地主机上打开,但是它会在弹出此错误时立即关闭。在package.json中,npm 运行 server和npm 运行 client都是在输入npm start时同时触发的
[1] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[1] at new NodeError (node:internal/errors:329:5)
[1] at ServerResponse.setHeader (node:_http_outgoing:573:11)
[1] at ServerResponse.header (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:771:10)
[1] at ServerResponse.send (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:170:12)
[1] at ServerResponse.json (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:267:15)
[1] at ServerResponse.send (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:158:21)
[1] at D:\UCL\real-time-tweet-streamer\server\server.js:77:9
[1] at processTicksAndRejections (node:internal/process/task_queues:94:5) {
[1] code: 'ERR_HTTP_HEADERS_SENT'
[1] }
[1] npm ERR! code 1
[1] npm ERR! path D:\UCL\real-time-tweet-streamer
[1] npm ERR! command failed
[1] npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c node server/server.js
这是来自 server.js
的代码
const express = require("express");
const bodyParser = require("body-parser");
const util = require("util");
const request = require("request");
const path = require("path");
const socketIo = require("socket.io");
const http = require("http");
const app = express();
let port = process.env.PORT || 3000;
const post = util.promisify(request.post);
const get = util.promisify(request.get);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const server = http.createServer(app);
const io = socketIo(server);
const BEARER_TOKEN = process.env.TWITTER_BEARER_TOKEN;
let timeout = 0;
const streamURL = new URL(
"https://api.twitter.com/2/tweets/search/stream?tweet.fields=context_annotations&expansions=author_id"
);
const rulesURL = new URL(
"https://api.twitter.com/2/tweets/search/stream/rules"
);
const errorMessage = {
title: "Please Wait",
detail: "Waiting for new Tweets to be posted...",
};
const authMessage = {
title: "Could not authenticate",
details: [
`Please make sure your bearer token is correct.
If using Glitch, remix this app and add it to the .env file`,
],
type: "https://developer.twitter.com/en/docs/authentication",
};
const sleep = async (delay) => {
return new Promise((resolve) => setTimeout(() => resolve(true), delay));
};
app.get("/api/rules", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(400).send(authMessage);
}
const token = BEARER_TOKEN;
const requestConfig = {
url: rulesURL,
auth: {
bearer: token,
},
json: true,
};
try {
const response = await get(requestConfig);
if (response.statusCode !== 200) {
if (response.statusCode === 403) {
res.status(403).send(response.body);
} else {
throw new Error(response.body.error.message);
}
}
res.send(response);
} catch (e) {
res.send(e);
}
});
app.post("/api/rules", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(400).send(authMessage);
}
const token = BEARER_TOKEN;
const requestConfig = {
url: rulesURL,
auth: {
bearer: token,
},
json: req.body,
};
try {
const response = await post(requestConfig);
if (response.statusCode === 200 || response.statusCode === 201) {
res.send(response);
} else {
throw new Error(response);
}
} catch (e) {
res.send(e);
}
});
const streamTweets = (socket, token) => {
let stream;
const config = {
url: streamURL,
auth: {
bearer: token,
},
timeout: 31000,
};
try {
const stream = request.get(config);
stream
.on("data", (data) => {
try {
const json = JSON.parse(data);
if (json.connection_issue) {
socket.emit("error", json);
reconnect(stream, socket, token);
} else {
if (json.data) {
socket.emit("tweet", json);
} else {
socket.emit("authError", json);
}
}
} catch (e) {
socket.emit("heartbeat");
}
})
.on("error", (error) => {
// Connection timed out
socket.emit("error", errorMessage);
reconnect(stream, socket, token);
});
} catch (e) {
socket.emit("authError", authMessage);
}
};
const reconnect = async (stream, socket, token) => {
timeout++;
stream.abort();
await sleep(2 ** timeout * 1000);
streamTweets(socket, token);
};
io.on("connection", async (socket) => {
try {
const token = BEARER_TOKEN;
io.emit("connect", "Client connected");
const stream = streamTweets(io, token);
} catch (e) {
io.emit("authError", authMessage);
}
});
console.log("NODE_ENV is", process.env.NODE_ENV);
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../build")));
app.get("*", (request, res) => {
res.sendFile(path.join(__dirname, "../build", "index.html"));
});
} else {
port = 3001;
}
server.listen(port, () => console.log(`Listening on port ${port}`));
如果有人能帮助我了解错误所在以及存在哪些可能的解决方案,我将不胜感激。
在您发送响应的每个地方之后,您需要 return
这样您的函数就不会继续执行并导致它尝试发送另一个响应。我在 /api/rules
路由中看到多个地方需要在发送响应后添加 return
因为如果你不这样做,它会执行 res.end(response)
即使你已经发送了回复。这就是导致您看到的错误的原因。
我正在 node.js 和 react.js 上创建实时 Twitter 流媒体应用程序,并且在 npm 启动时遇到此错误。我已经修复了 package.json 文件,该应用程序确实在本地主机上打开,但是它会在弹出此错误时立即关闭。在package.json中,npm 运行 server和npm 运行 client都是在输入npm start时同时触发的
[1] Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[1] at new NodeError (node:internal/errors:329:5)
[1] at ServerResponse.setHeader (node:_http_outgoing:573:11)
[1] at ServerResponse.header (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:771:10)
[1] at ServerResponse.send (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:170:12)
[1] at ServerResponse.json (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:267:15)
[1] at ServerResponse.send (D:\UCL\real-time-tweet-streamer\node_modules\express\lib\response.js:158:21)
[1] at D:\UCL\real-time-tweet-streamer\server\server.js:77:9
[1] at processTicksAndRejections (node:internal/process/task_queues:94:5) {
[1] code: 'ERR_HTTP_HEADERS_SENT'
[1] }
[1] npm ERR! code 1
[1] npm ERR! path D:\UCL\real-time-tweet-streamer
[1] npm ERR! command failed
[1] npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c node server/server.js
这是来自 server.js
的代码const express = require("express");
const bodyParser = require("body-parser");
const util = require("util");
const request = require("request");
const path = require("path");
const socketIo = require("socket.io");
const http = require("http");
const app = express();
let port = process.env.PORT || 3000;
const post = util.promisify(request.post);
const get = util.promisify(request.get);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const server = http.createServer(app);
const io = socketIo(server);
const BEARER_TOKEN = process.env.TWITTER_BEARER_TOKEN;
let timeout = 0;
const streamURL = new URL(
"https://api.twitter.com/2/tweets/search/stream?tweet.fields=context_annotations&expansions=author_id"
);
const rulesURL = new URL(
"https://api.twitter.com/2/tweets/search/stream/rules"
);
const errorMessage = {
title: "Please Wait",
detail: "Waiting for new Tweets to be posted...",
};
const authMessage = {
title: "Could not authenticate",
details: [
`Please make sure your bearer token is correct.
If using Glitch, remix this app and add it to the .env file`,
],
type: "https://developer.twitter.com/en/docs/authentication",
};
const sleep = async (delay) => {
return new Promise((resolve) => setTimeout(() => resolve(true), delay));
};
app.get("/api/rules", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(400).send(authMessage);
}
const token = BEARER_TOKEN;
const requestConfig = {
url: rulesURL,
auth: {
bearer: token,
},
json: true,
};
try {
const response = await get(requestConfig);
if (response.statusCode !== 200) {
if (response.statusCode === 403) {
res.status(403).send(response.body);
} else {
throw new Error(response.body.error.message);
}
}
res.send(response);
} catch (e) {
res.send(e);
}
});
app.post("/api/rules", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(400).send(authMessage);
}
const token = BEARER_TOKEN;
const requestConfig = {
url: rulesURL,
auth: {
bearer: token,
},
json: req.body,
};
try {
const response = await post(requestConfig);
if (response.statusCode === 200 || response.statusCode === 201) {
res.send(response);
} else {
throw new Error(response);
}
} catch (e) {
res.send(e);
}
});
const streamTweets = (socket, token) => {
let stream;
const config = {
url: streamURL,
auth: {
bearer: token,
},
timeout: 31000,
};
try {
const stream = request.get(config);
stream
.on("data", (data) => {
try {
const json = JSON.parse(data);
if (json.connection_issue) {
socket.emit("error", json);
reconnect(stream, socket, token);
} else {
if (json.data) {
socket.emit("tweet", json);
} else {
socket.emit("authError", json);
}
}
} catch (e) {
socket.emit("heartbeat");
}
})
.on("error", (error) => {
// Connection timed out
socket.emit("error", errorMessage);
reconnect(stream, socket, token);
});
} catch (e) {
socket.emit("authError", authMessage);
}
};
const reconnect = async (stream, socket, token) => {
timeout++;
stream.abort();
await sleep(2 ** timeout * 1000);
streamTweets(socket, token);
};
io.on("connection", async (socket) => {
try {
const token = BEARER_TOKEN;
io.emit("connect", "Client connected");
const stream = streamTweets(io, token);
} catch (e) {
io.emit("authError", authMessage);
}
});
console.log("NODE_ENV is", process.env.NODE_ENV);
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../build")));
app.get("*", (request, res) => {
res.sendFile(path.join(__dirname, "../build", "index.html"));
});
} else {
port = 3001;
}
server.listen(port, () => console.log(`Listening on port ${port}`));
如果有人能帮助我了解错误所在以及存在哪些可能的解决方案,我将不胜感激。
在您发送响应的每个地方之后,您需要 return
这样您的函数就不会继续执行并导致它尝试发送另一个响应。我在 /api/rules
路由中看到多个地方需要在发送响应后添加 return
因为如果你不这样做,它会执行 res.end(response)
即使你已经发送了回复。这就是导致您看到的错误的原因。