在纯前端项目中接入OpenAI API驱动的对话能力,本质是通过HTTP请求把用户输入发给模型,再把模型回复展示在页面上。由于浏览器直接调用官方接口会面临跨域限制和密钥泄露风险,通常我们需要一个简单的服务端做中转。下面通过一个完整示例,说明如何从零搭建一个可运行的网页对话应用。

一、整体架构与核心思路
整套系统分为两层:前端HTML页面负责采集用户输入、展示对话记录;后端代理服务持有API Key,接收前端请求后转发给OpenAI接口。这样密钥不会出现在客户端代码中,也绕开了浏览器的跨域策略。
对话数据以消息数组形式传递,每条消息包含角色(system、user或assistant)和内容。模型依据历史消息生成连贯回复。如果希望体验更接近ChatGPT的逐字输出,可以开启流式响应,前端通过读取数据流逐步渲染文本。
1.1 为什么需要后端代理
OpenAI的接口默认不允许浏览器跨域直接访问,且API Key若写在前端会被任意用户提取并盗刷。后端代理把密钥放在服务器环境变量中,仅暴露一个本地接口给前端,安全性大幅提升。
代理服务也可以用Serverless函数(如Cloudflare Workers、Vercel Edge)实现,无需长期运行服务器。对个人项目而言,一个几十行代码的Node服务已足够支撑基础对话。
二、前端HTML与JavaScript实现
前端部分包含一个消息列表容器、输入框和发送按钮。点击发送后,把新消息追加到本地数组,再请求后端代理。下面给出最小可用的页面结构。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>网页版ChatGPT</title>
<style>
.msg { margin: 8px 0; padding: 6px 10px; border-radius: 6px; }
.user { background: #e3f2fd; }
.bot { background: #f1f8e9; }
</style>
</head>
<body>
<div id="list"></div>
<input id="text" placeholder="说点什么" />
<button onclick="send()">发送</button>
<script src="app.js"></script>
</body>
</html>
2.1 对话逻辑代码
app.js维护一个messages数组,每次发送都将用户输入以user角色推入,然后调用后端。收到回复后以assistant角色追加并重新渲染列表。
let messages = [{ role: 'system', content: '你是一个友好的助手' }];
async function send() {
const input = document.getElementById('text');
const val = input.value.trim();
if (!val) return;
messages.push({ role: 'user', content: val });
input.value = '';
render();
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
const data = await res.json();
messages.push({ role: 'assistant', content: data.reply });
render();
}
function render() {
const list = document.getElementById('list');
list.innerHTML = '';
messages.forEach(m => {
const div = document.createElement('div');
div.className = 'msg ' + (m.role === 'user' ? 'user' : 'bot');
div.textContent = m.content;
list.appendChild(div);
});
}
上述代码采用普通JSON响应,逻辑直观。若想做流式输出,可将fetch改为读取response.body的Reader,按块解析SSE格式,这里先掌握基础版。
注意前端只和同源的/api/chat通信,不直接接触OpenAI域名,因此部署时前后端需同域或配置反向代理。
三、后端代理服务示例
用Node.js和Express写一个极简代理,把请求转发到OpenAI的chat接口。密钥从环境变量读取,绝不返回给前端。
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
const { messages } = req.body;
const apiRes = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + process.env.OPENAI_KEY
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: messages
})
});
const json = await apiRes.json();
res.json({ reply: json.choices[0].message.content });
});
app.listen(3000, () => console.log('代理运行在3000端口'));
3.1 错误处理与限额
真实项目中应捕获网络异常,并在json无choices时返回友好提示。同时可在代理层加简单计数,防止单个IP高频调用导致账单暴涨。
如果计划公开部署,建议加上用户会话隔离,避免不同访客的对话历史互相串台。可以用sessionId映射各自的messages数组。
四、流式输出进阶
为了让回复像ChatGPT那样逐字出现,后端改为返回text/event-stream,前端用EventSource或fetch流读取。下面给出前端流式渲染片段。
async function sendStream() {
const val = document.getElementById('text').value;
messages.push({ role: 'user', content: val });
const res = await fetch('/api/chat-stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages })
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let botMsg = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
botMsg += decoder.decode(value);
// 将botMsg追加到页面最后一条bot消息中
}
messages.push({ role: 'assistant', content: botMsg });
}
后端只需把OpenAI返回的stream:true响应体直接pipe给客户端,并设好响应头。这样用户能实时看到模型思考过程,体验更自然。
集成完成后,你可以把HTML托管在任意静态空间,代理跑在小型云主机或Serverless上,即可拥有一个私人的网页对话机器人。
OpenAI_APIChatGPT_integrationHTML_webpage修改时间:2026-08-06 08:36:38