从前端 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 开发的大门就已经向你敞开了。


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

相关推荐
mldong21 分钟前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排1 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰3 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端