从前端 HTTP 请求到 LLM 接口调用:一篇文章带你彻底搞懂

无论你是刚入门的前端新手,还是正在探索 AI 应用开发的工程师,理解 HTTP 请求都是绕不开的基本功。本文将通过一个完整的 Demo 项目,从最基础的本地数据请求讲起,一步步带你掌握从前端发送 HTTP 请求、理解前后端分离架构,到最终调用大模型 API 的完整链路。


一、项目概览:三个模块,一条链路

整个 Demo 项目由三个模块组成,恰好覆盖了前端 HTTP 请求的三大典型场景:

css 复制代码
demo1/
├── backend/          ← 后端:用 json-server 快速搭建 REST API
├── frontend/         ← 前端:从后端获取数据并渲染到页面
└── demo/             ← AI实战:调用 DeepSeek 大模型 API
模块 角色 核心能力
backend 数据提供方 提供 http://127.0.0.1:3000/friends 接口
frontend 数据消费方 fetch 获取数据 → 渲染 HTML 表格
demo AI 接口调用 fetch POST 调用大模型,拿到 AI 回复

下面我们逐个拆解,从最简单的开始。


二、Backend:三分钟搭建一个后端服务

很多人觉得"后端"很神秘,其实一个能用的后端,三分钟就能搭好。

2.1 数据准备

json 复制代码
// data.json --- 这就是我们的"数据库"
{
  "friends": [
    { "id": 1, "name": "张三", "age": 18 },
    { "id": 2, "name": "李四", "age": 20 }
  ]
}

一个 JSON 文件,两个好友对象,简单到不能再简单。

2.2 一行命令启动服务

json 复制代码
// package.json
{
  "scripts": {
    "dev": "json-server --watch data.json --port 3000"
  }
}

json-server 是一个零配置的 REST API 工具,它做了什么?

  1. 读取 data.json 文件
  2. 自动生成 RESTful 接口------GET /friends 返回好友列表
  3. 监听 3000 端口,等待请求

一条 npm run dev,你的后端就上线了。访问 http://127.0.0.1:3000/friends,浏览器直接返回 JSON 数据。

💡 核心认知:后端本质上就是一个"等着被问话的程序"。前端问一句(发请求),后端答一句(返回数据)。


三、服务器到底是什么?

说起"服务器",别被这个词吓到。拆开看,就两层意思:

3.1 硬件层面

服务器本质上就是一台一直开着的电脑,在网络中有一个唯一的 IP 地址,负责接收和处理请求。

3.2 软件层面

服务器上运行着特定的软件来响应请求:

  • Node.js + json-server:Demo 中用的方案,轻量快速
  • Java(Spring Boot):企业级后端常用
  • Nginx:高性能 Web 服务器,常用于反向代理

3.3 IP、端口、域名、DNS

当你访问 http://127.0.0.1:3000/friends,这个地址每个部分都有含义:

arduino 复制代码
http://127.0.0.1:3000/friends
  │        │       │      │
  协议     IP地址   端口   路径(endpoint)
  • IP 地址 :网络世界中服务器的唯一标识。127.0.0.1(也叫 localhost)永远指向你自己的电脑
  • 端口号 :一台服务器上可能跑着多个服务,端口号就是用来区分它们的。:3000 表示"我要找 3000 端口上的那个服务"。常见端口:80(HTTP)、443(HTTPS)、8080(Java)、3000(前端开发)
  • 域名www.baidu.com110.242.68.66 好记一万倍,域名就是 IP 的"别名"
  • DNS :把域名翻译成 IP 地址的"翻译官"。你输入 www.baidu.com,DNS 帮你查出背后的 IP,然后浏览器才能找到服务器

四、Frontend:从数据获取到页面渲染

有了后端,前端怎么拿数据?来看 frontend 模块的完整代码。

4.1 页面骨架

html 复制代码
<!-- frontend/index.html -->
<header>
  <h1>前端发送 HTTP 请求</h1>
</header>
<main>
  <table>
    <thead>
      <tr>
        <th>id</th>
        <th>name</th>
        <th>age</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>
</main>
<script src="./main.js"></script>

一个标题 + 一张空表格,表格内容留给 JavaScript 动态填充。这就是数据驱动视图的雏形------HTML 只定义结构,数据由 JS 负责填入。

4.2 核心逻辑:fetch + 渲染

javascript 复制代码
// frontend/main.js
let friends = [];

// 第一步:从后端获取数据
async function loadData() {
  const endpoint = 'http://127.0.0.1:3000/friends';
  const res = await fetch(endpoint);
  const data = await res.json();
  console.log(data);
  return data;
}

// 第二步:把数据渲染成表格行
function renderData(friends) {
  const oBody = document.querySelector('table tbody');
  if (friends.length > 0) {
    oBody.innerHTML = friends.map(function (friend) {
      return `
        <tr>
          <td>${friend.id}</td>
          <td>${friend.name}</td>
          <td>${friend.age}</td>
        </tr>
      `;
    }).join('');
  }
}

// 第三步:编排整个流程
async function init() {
  console.log('init start');
  const friends = await loadData();  // 等数据回来
  console.log(friends);
  renderData(friends);               // 数据到了,渲染
}

init();

三个函数,职责分明

函数 职责 模式
loadData() 数据获取 纯 I/O,只管拿数据
renderData() 视图渲染 数据 → DOM
init() 流程编排 串联前两步,控制执行顺序

这就是关注点分离(Separation of Concerns)------每个函数只做一件事,组合起来完成复杂任务。

4.3 数据转换的核心:map + join

javascript 复制代码
friends.map(function (friend) {
  return `<tr>...</tr>`;
}).join('');

这条链式调用干了什么?

  1. map :遍历数组中的每个 friend 对象,把它转换 成一行 <tr> HTML 字符串。[{id:1,name:'张三'}, ...]['<tr>...张三...</tr>', '<tr>...李四...</tr>']
  2. join('') :把字符串数组拼接成一个大字符串。['<tr>...</tr>', '<tr>...</tr>']'<tr>...</tr><tr>...</tr>'
  3. innerHTML =:一次性写入 DOM,浏览器渲染出两行表格

📌 这其实就是 React / Vue 虚拟 DOM diff 之前最原始的渲染方式------数据变了,重新生成 HTML,整体替换。理解了这一点,就理解了为什么现代框架需要虚拟 DOM 来优化性能。


五、HTTP 请求的完整结构

上面 loadData() 里的 fetch(endpoint) 是一个最简单的 GET 请求。但一个完整的 HTTP 请求,其实由三部分组成:

bash 复制代码
┌──────────────────────────────────────────┐
│               HTTP 请求报文                │
├────────────┬─────────────────────────────┤
│  请求行     │  POST /chat/completions     │
│            │  HTTP/1.1                   │
├────────────┼─────────────────────────────┤
│  请求头     │  Content-Type: app/json     │
│            │  Authorization: Bearer xxx   │
├────────────┼─────────────────────────────┤
│  请求体     │  {"model":"...","messages":} │
└────────────┴─────────────────────────────┘

5.1 请求行

方法 + URL + HTTP版本,告诉服务器"我要干什么":

  • GET ------ 拿数据
  • POST ------ 提交数据
  • PUT ------ 更新数据
  • DELETE ------ 删除数据

5.2 请求头

附加的元信息,告诉服务器"我是谁""我能接收什么":

  • Content-Type: application/json ------ 我发的数据是 JSON 格式
  • Authorization: Bearer sk-xxx ------ 这是我的身份凭证(API Key)

5.3 请求体

实际要发送的数据。关键点 :HTTP 只能传输字符串,JavaScript 对象必须用 JSON.stringify() 序列化后才能发送。


六、Demo:调用大模型 API 实战

这是最激动人心的部分------用 fetch 直接调用 DeepSeek 大模型,拿到 AI 的智能回复。

6.1 完整代码

javascript 复制代码
// demo/script.js
// ① 请求行:POST + URL
const endpoint = 'https://api.deepseek.com/chat/completions';

// ② 请求头:告诉 API 我是谁、发什么格式
const headers = {
  'Content-Type': 'application/json',
  'Authorization': `Bearer sk-xxxxxxxxxxxxxxxxxx`,
};

// ③ 请求体:我要问什么
const payload = {
  model: 'deepseek-v4-flash',    // 选择模型(flash = 更快更便宜)
  messages: [
    {
      role: 'system',
      content: 'You are a helpful assistant.',
    },
    {
      role: 'user',
      content: '你好, Deepseek',
    },
  ],
};

try {
  const response = await fetch(endpoint, {
    method: 'POST',              // 用 POST,因为要提交数据
    headers,
    body: JSON.stringify(payload), // 对象 → JSON 字符串
  });
  const data = await response.json();
  // 拿到 AI 回复,渲染到页面
  document.getElementById('reply').innerHTML = data.choices[0].message.content;
} catch (err) {
  console.error(err);
}

6.2 流程拆解

sql 复制代码
用户消息 "你好, Deepseek"
        │
        ▼
┌──────────────────────────────┐
│  构造 Messages:              │
│  - system: 设定 AI 人设       │
│  - user: 用户实际输入         │
└──────────────────────────────┘
        │
        ▼
┌──────────────────────────────┐
│  fetch POST                  │
│  → api.deepseek.com          │
│  → JSON.stringify(payload)   │
└──────────────────────────────┘
        │
        ▼
┌──────────────────────────────┐
│  服务器处理(大模型推理)       │
└──────────────────────────────┘
        │
        ▼
┌──────────────────────────────┐
│  返回 response               │
│  data.choices[0]             │
│    .message.content          │
│  → AI 的回复文本              │
└──────────────────────────────┘
        │
        ▼
  渲染到页面 #reply 元素

6.3 关键细节解读

Messages 结构------和 AI 对话的核心约定:

role 含义 谁说的话
system 系统提示词 你(开发者)给 AI 设定的行为准则
user 用户消息 用户输入的问题
assistant AI 回复 AI 的历史回答(多轮对话时使用)

为什么选 deepseek-v4-flash ------ 这是 DeepSeek 的轻量模型,速度快、价格低,适合日常使用。如果对质量要求更高,可以换成 deepseek-v4-pro 等更强大的版本。

try/catch 为什么必须写? ------ 网络请求充满了不确定性:服务器可能宕机、网络可能断开、API Key 可能过期......try/catch 确保即使出错了,程序也不会崩溃,而是优雅地处理异常。


七、前后端分离,到底分的是什么?

把 frontend + backend 的交互画成一张图:

javascript 复制代码
┌──────────────────┐           HTTP            ┌──────────────────┐
│     Frontend      │ ────────────────────────> │     Backend       │
│                   │                           │                   │
│  index.html       │  fetch("http://127.0.0.1 │  json-server      │
│  main.js          │    :3000/friends")        │  data.json        │
│                   │                           │  端口 :3000        │
│  负责:           │ <──────────────────────── │                   │
│  · 页面展示       │     返回 JSON 数据         │  负责:           │
│  · 用户交互       │                           │  · 数据存储       │
│  · 数据渲染       │                           │  · 业务逻辑       │
└──────────────────┘                           └──────────────────┘

这就是前后端分离的核心思想:

  • 前端只管展示和交互,不碰数据库,不写业务逻辑
  • 后端只管数据和逻辑,不画页面,不处理 DOM
  • 两者通过 HTTP 协议 通信,各司其职,互不干扰

延伸:两种架构模式

B/S 架构(Browser / Server):浏览器 ↔ 服务器。你打开网页,浏览器向服务器发请求,服务器返回 HTML/CSS/JS。这是我们前端最常打交道的模式。

C/S 架构(Client / Server):客户端不限于浏览器------可以是 iOS App、Android App、桌面软件、小程序。它们都通过 HTTP 请求调用同一套后端 API。

markdown 复制代码
         ┌──────────┐
         │  浏览器   │──┐
         └──────────┘  │
         ┌──────────┐  │    HTTP/HTTPS     ┌──────────────┐
         │  iOS App │──┼──────────────────>│   后端服务    │
         └──────────┘  │                   │  (统一 API)   │
         ┌──────────┐  │                   └──────────────┘
         │ Android  │──┘
         └──────────┘

八、async/await:让异步代码像同步一样好读

JavaScript 是单线程的,但网络请求需要时间。如果同步等待,页面会卡死。所以我们需要异步编程

async/await 是目前最优美的异步方案:

javascript 复制代码
// 看这段代码,就读它的"执行顺序"
async function init() {
  console.log('init start');         // 1️⃣ 先执行
  const friends = await loadData();  // 2️⃣ 等数据(不阻塞 UI)
  console.log(friends);              // 3️⃣ 数据到了才往后走
  renderData(friends);               // 4️⃣ 渲染
}

init();
// init end 永远不会在 friends 之前打印

await 做了什么? ------ 它把异步操作"暂停"在原地,等 Promise 完成后再继续往下走。但注意------它只暂停当前 async 函数的执行,不会阻塞主线程,用户依然可以滚动页面、点击按钮。

⚠️ 铁律await 只能在 async 函数内部使用。


九、前端发送 HTTP 请求的两种方式

9.1 XMLHttpRequest ------ 经典方案

XMLHttpRequest(XHR)是 Ajax 时代的基石,虽然现在很少直接写,但理解它有助于面试和读老项目代码:

javascript 复制代码
const xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:3000/friends');
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log(data);
  }
};
xhr.send();

缺点:回调嵌套、代码冗长、没有 Promise 原生支持。

9.2 fetch ------ 现代标准

javascript 复制代码
const res = await fetch('http://127.0.0.1:3000/friends');
const data = await res.json();

两行搞定。基于 Promise,天然支持 async/await,语法清爽。

📌 面试高频 :fetch 对比 XHR ------ ① fetch 基于 Promise,可链式调用;② fetch 默认不携带 cookie(需要设置 credentials: 'include');③ fetch 遇到 404/500 不会 reject(需要检查 response.ok)。


十、调用 LLM 接口的另一种姿势:OpenAI SDK

除了用 fetch 裸写请求,实际项目中还可以用 OpenAI SDK。因为 DeepSeek 兼容 OpenAI 的接口规范,所以可以直接用 OpenAI 的 SDK 来调用:

javascript 复制代码
import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://api.deepseek.com',
  apiKey: 'sk-xxxxxxxxxxxxxxxxxx',
});

const completion = await client.chat.completions.create({
  model: 'deepseek-v4-flash',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: '你好, Deepseek' },
  ],
});

console.log(completion.choices[0].message.content);

SDK vs 裸 fetch,怎么选?

维度 fetch 裸写 OpenAI SDK
依赖 零依赖 需要安装 openai
控制力 完全掌控每个细节 SDK 封装了认证、重试等
学习价值 深入理解 HTTP 协议 快速开发、类型安全
适用场景 任意 HTTP API OpenAI 兼容接口

💡 建议:先用 fetch 裸写一遍,搞清楚 HTTP 请求的底层是怎么回事;实际项目中再用 SDK 提效。先懂原理,再求效率。


十一、知识地图:一张图收尾

scss 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                     前端 HTTP 请求知识体系                        │
├──────────────┬──────────────────────────────────────────────────┤
│  请求方式     │  fetch (现代标准) / XMLHttpRequest (经典方案)      │
├──────────────┼──────────────────────────────────────────────────┤
│  报文结构     │  请求行 (方法+URL) + 请求头 (元信息) + 请求体 (数据) │
├──────────────┼──────────────────────────────────────────────────┤
│  架构模式     │  B/S (浏览器-服务器) / C/S (客户端-服务器) / 前后端分离│
├──────────────┼──────────────────────────────────────────────────┤
│  网络基础     │  IP 地址 → 端口号 → 域名 → DNS 解析               │
├──────────────┼──────────────────────────────────────────────────┤
│  异步控制     │  async/await 让异步代码像同步一样好读              │
├──────────────┼──────────────────────────────────────────────────┤
│  数据转换     │  JSON.stringify ↔ JSON.parse / map + join 渲染   │
├──────────────┼──────────────────────────────────────────────────┤
│  实战场景     │  ① 本地数据获取 ② AI 大模型 API 调用              │
├──────────────┼──────────────────────────────────────────────────┤
│  进阶方向     │  OpenAI SDK / 流式响应 (SSE) / WebSocket          │
└──────────────┴──────────────────────────────────────────────────┘

十二、写在最后

我们从零搭建了一个后端服务,用前端 fetch 拿到数据并渲染成表格,最后还成功调用了一次大模型 API,拿到了 AI 的智能回复。

回顾一下这条链路:

scss 复制代码
Backend (json-server, 提供数据)
      ↑
      │  HTTP GET
      │
Frontend (fetch + map渲染, 展示数据)

Demo (fetch POST, 调用大模型 API, 获取 AI 回复)

你学到了什么:

  • ✅ 用 json-server 快速搭建后端 API
  • ✅ fetch 的 GET 和 POST 两种请求方式
  • ✅ HTTP 请求报文的三要素(请求行、请求头、请求体)
  • ✅ IP、端口、域名、DNS 的基础概念
  • ✅ 前后端分离的架构思想(B/S、C/S)
  • async/await 优雅控制异步流程
  • ✅ JSON 数据→HTML 视图的 map + join 渲染模式
  • ✅ 调用大模型 API 的完整流程(Messages、API Key、模型选择)
  • ✅ OpenAI SDK 与裸 fetch 的优劣对比

下一步你可以:

  • 去 MDN 翻阅 fetch 的官方文档,了解更多配置选项(超时、取消请求、跨域等)
  • 亲手跑通 json-server,感受前后端交互的全过程
  • 尝试调用不同的大模型 API(通义千问、智谱 GLM、Moonshot 等),对比响应差异
  • 思考进阶问题:数据量大时如何优化渲染?如何实现流式响应(一个字一个字蹦出来)?

前端和后端的 HTTP 通信,就像打电话------前端拨号(发请求),后端接听(处理),然后后端把话传回来(返回响应)。理解了这层关系,Web 开发的大门就已经向你敞开了。


本文所有示例代码均可独立运行,建议边读边练。动手,永远是学编程最快的方式。🚀

相关推荐
先吃饱再说1 小时前
从 WeUI 按钮组件学 BEM 命名规范:让 CSS 不再难维护
前端·代码规范
陈_杨1 小时前
鸿蒙APP开发-带你走进旧物集的时间线与收藏管理
前端·javascript
拂尘子1 小时前
前端屎山代码救星:这个 MCP 把 7000 行页面压成 60 行骨架,Token 直接省掉 90%+
前端·ai编程·mcp
小雨下雨的雨1 小时前
月相分析工具鸿蒙PC Electron框架技术实现详解
前端·javascript·华为·electron
布依前端2 小时前
基于 Vue 3 的 Tiptap 富文本编辑器实践:tiptap-editor-vue3 项目介绍
前端·javascript·vue.js
阿狸猿2 小时前
论负载均衡技术在 Web 系统中的应用
运维·前端·负载均衡
橘猫走江湖2 小时前
Cursor Vibe Coding 开发指南
前端
因_崔斯汀2 小时前
网页为什么需要框架?
前端
前端 贾公子2 小时前
Tailwind CSS `shrink-0`是啥意思?
前端