前端HTTP请求完全指南:从基础到LLM接口调用实战

前言

在当今的前后端分离开发模式下,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请求的各个方面:

  1. 基础方法:XMLHttpRequest和Fetch API

  2. 编程模式:前后端分离、异步编程、B/S架构

  3. 网络基础:IP、端口、域名、API端点

  4. 实战应用:LLM接口调用(OpenAI SDK + Fetch)

  5. 数据处理:JSON转换、错误处理

  6. 性能优化:缓存、防抖节流

掌握这些知识,你将能够:

  • 灵活运用各种HTTP请求方式

  • 高效调用LLM等第三方API

  • 构建健壮的前端应用

  • 优化网络请求性能

相关推荐
默_笙1 小时前
🍳 受控组件和非受控组件,我纠结了一整天,最后用"房东和租客"讲明白了
前端·javascript
烬羽1 小时前
CSS 三栏布局:`margin-left: -100%` 凭什么把侧栏「拽」回上一行?彻底搞懂负 margin
css·面试·前端框架
半个落月1 小时前
React 受控组件与非受控组件详解:从输入框到表单校验
前端·react.js
BreezeJiang1 小时前
写了 display:flex,为什么三栏布局还没完成?
前端·css
Hilaku1 小时前
高级前端如何优雅地拒绝不合理的产品需求?
前端·javascript·程序员
独立开阀者_FwtCoder1 小时前
这次更新:换新主题,同时把好计划分享出去
前端·javascript·vue.js
禁止摆烂_才浅1 小时前
JavaScript WebAPI(进阶)高频面试题
前端·javascript·面试
禁止摆烂_才浅1 小时前
ES6+ 高频面试题
前端·javascript·面试
J船长1 小时前
Vscode/Cursor开了太多项目怕搞混?上Peacock啊
前端