前言
在当今的前后端分离开发模式下,HTTP请求是前端与后端通信的基石。随着大语言模型(LLM)的普及,掌握如何高效调用HTTP接口变得尤为重要。本文将带你深入了解前端HTTP请求的多种方式,并通过实战案例展示如何使用OpenAI SDK和原生fetch调用LLM接口。
一、前端发送HTTP请求的常见方式
1.1 XMLHttpRequest(XHR)
XMLHttpRequest是浏览器最早提供的异步通信API,虽然较为底层,但功能完善。
bash
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(JSON.parse(xhr.responseText));
}
};
xhr.send();
1.2 Fetch API(现代推荐)
Fetch是ES6引入的现代替代方案,基于Promise设计,语法更简洁。
bash
// GET请求
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// POST请求
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data));
1.3 第三方库(Axios等)
在实际项目中,我们常使用封装更完善的第三方库。
bash
// Axios示例
axios.get('https://api.example.com/data')
.then(response => console.log(response.data))
.catch(error => console.error(error));
二、编程模式解析
2.1 前后端分离架构
这是一种将前端UI层与后端业务逻辑层分离的开发模式:
-
前端:负责界面渲染、用户交互
-
后端:提供RESTful API或GraphQL接口
-
通信:通过HTTP/HTTPS协议进行数据交换
2.2 异步编程与Async/Await
bash
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('请求失败:', error);
}
}
核心优势:
-
非阻塞:不会阻塞主线程
-
流畅体验:用户操作不受影响
-
资源高效:充分利用浏览器性能
2.3 B/S vs C/S架构
| 架构类型 | 特点 | 代表应用 |
|---|---|---|
| B/S(Browser/Server) | 无需安装,通过浏览器访问 | Web应用、小程序 |
| C/S(Client/Server) | 需要安装客户端,功能更强大 | 手机App、桌面软件 |
三、服务器与网络基础
3.1 理解服务器地址
bash
http://127.0.0.1:3000/api/v1/chat
├───┬─── ─┬─ ──┬─── ───┬─────
│ │ │ │ └─ API端点路径
│ │ │ └─ 端口号(3000)
│ │ └─ IP地址(本地回环)
│ └─ 协议(HTTP)
└─ 域名(如:www.baidu.com)
-
IP地址:网络层的唯一标识
-
域名:用户友好的访问方式
-
DNS解析:将域名转换为IP地址
3.2 API端点(Endpoint)
API端点是服务的具体访问地址,通常遵循RESTful设计规范:
bash
GET /api/users # 获取用户列表
POST /api/users # 创建用户
GET /api/users/:id # 获取特定用户
PUT /api/users/:id # 更新用户
DELETE /api/users/:id # 删除用户
四、实战:调用LLM HTTP接口
4.1 使用OpenAI SDK(官方推荐)
bash
import OpenAI from 'openai';
const openai = new OpenAI({
apiKey: 'your-api-key',
baseURL: 'https://api.openai.com/v1'
});
async function chatWithGPT() {
try {
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: '你是一个AI助手' },
{ role: 'user', content: '请介绍一下HTTP协议' }
],
temperature: 0.7,
max_tokens: 1000
});
console.log(completion.choices[0].message.content);
return completion;
} catch (error) {
console.error('LLM调用失败:', error);
throw error;
}
}
4.2 使用原生Fetch调用
bash
async function callLLMWithFetch() {
const API_URL = 'https://api.openai.com/v1/chat/completions';
const API_KEY = 'your-api-key';
const requestData = {
model: 'gpt-3.5-turbo',
messages: [
{ role: 'system', content: '你是一个专业的编程助手' },
{ role: 'user', content: '写一个JavaScript的HTTP请求示例' }
],
temperature: 0.7,
max_tokens: 500
};
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify(requestData)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('AI响应:', data.choices[0].message.content);
return data;
} catch (error) {
console.error('请求失败:', error);
throw error;
}
}
4.3 流式响应处理
对于LLM对话,流式响应能提供更好的用户体验:
bash
async function streamingLLMCall() {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: '讲一个故事' }],
stream: true // 启用流式响应
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(line => line.trim());
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const parsed = JSON.parse(data);
const content = parsed.choices[0]?.delta?.content;
if (content) {
fullText += content;
// 更新UI显示
console.log('实时输出:', content);
}
} catch (e) {
console.error('解析错误:', e);
}
}
}
}
console.log('完整响应:', fullText);
return fullText;
}
五、数据处理与渲染
5.1 数据转换示例
bash
// 假设从API获取的用户数据
const apiResponse = {
users: [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 28 }
]
};
// 转换为表格行数据
function renderUserTable(data) {
const tableRows = data.users.map(user =>
`<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.age}</td>
</tr>`
).join('');
document.getElementById('userTable').innerHTML = tableRows;
}
5.2 错误处理最佳实践
bash
async function robustAPICall(url, options = {}) {
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
// 检查HTTP状态
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
`HTTP ${response.status}: ${response.statusText}`
);
}
// 检查Content-Type
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return await response.json();
}
return await response.text();
} catch (error) {
console.error('API调用失败:', {
url,
error: error.message,
stack: error.stack
});
throw error;
}
}
六、性能优化建议
6.1 请求缓存策略
bash
class APICache {
constructor(ttl = 60000) { // 默认缓存1分钟
this.cache = new Map();
this.ttl = ttl;
}
async fetch(url, options = {}) {
const cacheKey = `${url}_${JSON.stringify(options)}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
const response = await fetch(url, options);
const data = await response.json();
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
}
}
6.2 请求防抖与节流
bash
// 防抖:用户停止输入后才发起请求
function debounce(fn, delay = 300) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
const searchWithDebounce = debounce(async (keyword) => {
const response = await fetch(`/api/search?q=${keyword}`);
return response.json();
}, 500);
七、常见问题与解决方案
7.1 CORS跨域问题
bash
// 服务端设置(Node.js + Express)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
7.2 超时处理
bash
function fetchWithTimeout(url, options = {}, timeout = 5000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('请求超时')), timeout)
)
]);
}
八、总结
本文全面介绍了前端HTTP请求的各个方面:
-
基础方法:XMLHttpRequest和Fetch API
-
编程模式:前后端分离、异步编程、B/S架构
-
网络基础:IP、端口、域名、API端点
-
实战应用:LLM接口调用(OpenAI SDK + Fetch)
-
数据处理:JSON转换、错误处理
-
性能优化:缓存、防抖节流
掌握这些知识,你将能够:
-
灵活运用各种HTTP请求方式
-
高效调用LLM等第三方API
-
构建健壮的前端应用
-
优化网络请求性能