☰
前后端连接最小Demo:原生fetch+Python http.server全链路解析
2026/10/12 7:23:51 网站建设 项目流程

“前后端怎么连起来的?”这是我被问得最多的问题之一,尤其是刚接触全栈概念的朋友。我在带新人的时候发现,很多人要么只写过前端页面,要么只在后端工具里见过接口返回,就是不知道这两个东西是怎么在浏览器和服务器之间完成一次真正的对话。后来我干脆做了一个最小可运行的demo:前端用一个纯HTML页面加原生fetch请求,后端用Python标准库自带的http.server,不依赖任何框架,把一次完整的请求-响应链路跑通。这个demo的核心价值就在于“原生”——每一行代码都对应一个真实的网络环节,没有框架替你遮住底层细节。这篇文章把它的完整实现、原理拆解、坑点排查全部写出来。

1. 先把最小demo的设计思路拆清楚

1.1 这个demo到底在演示什么

我们的目标不是造一个庞大系统,而是用最小代价验证一条最核心的链路:用户在前端页面上输入内容,点击按钮后浏览器向后端发起请求,后端收到请求后处理并返回一段数据,前端再把这段数据展示出来。

这个链路听起来很平常,但拆开来有三个关键环节,缺一不可:

  • 前端发起网络请求(用什么方式、发到哪里)
  • 后端监听并响应请求(监听哪个端口、如何处理路径)
  • 前端接收响应并渲染(如何解析数据、如何更新页面)

我的demo就围绕这三个环节来设计。前端只要一个HTML文件加一段JavaScript,后端只要一个Python文件,不装数据库、不引入框架、不做用户鉴权,全部代码加在一起不超过100行。这种“减法式”设计的好处是:你在运行的时候,每一帧网络日志、每一次调试报错,都能直接对应到代码中的某个位置,不会出现“看起来是这里的问题,但其实是框架内部某个机制在作祟”的尴尬情况。

1.2 为什么非要用原生实现

现在的开发环境里,前端有脚手架、后端有Web框架,前后端的“连接”在框架层面往往只是一行代码的事。但正因为太顺滑,很多人反而不知道这一行代码底下发生了什么。

举个例子,很多后端框架自带跨域处理中间件,前端框架自带代理转发配置,开发者只需要改两个配置项就看不到跨域报错,于是会把“启动成功”误解为“前后端已经连起来了”。我用原生方式实现,就是为了把这些封装一层层剥掉:

  • 前端用原生fetch,那么URL、请求头、请求体、Promise异步处理全都自己写,你必须知道自己要发什么。
  • 后端用标准库http.server,那么请求解析、路由判断、响应头设置、状态码返回全都要手写,你必须知道后端该回什么。

只有当你亲手写一次之后,再去用框架,你才会理解框架到底替你省了哪些事。这也是为什么我给新人做演示时坚持用原生方案:先见森林,再看树木。

1.3 方案选型的逻辑与取舍

在技术选型上,我最初考虑过用Node.js的http模块,也考虑过用Python的http.server,最终选了Python方案为主,同时把Node对照实现也附在文末。

选Python标准库的原因很简单:

  • 不需要安装任何第三方包,装好Python就能跑。
  • 内置的http.server模块代码量少,适合逐行讲解。
  • 大多数人对Python环境更熟悉,上手门槛更低。

放弃框架(比如Express和Flask)的原因前面已经说了。但注意“原生”不等于“土”,这个demo里的代码结构和真实项目并没有本质区别——前端依然发请求、后端依然监听路由、响应依然按MIME类型返回,只是框架把大部分模板代码省略了而已。理解了这套最小模型,再迁移到任何框架上面都是顺理成章的事。

2. HTTP协议:前后端连接的底层逻辑

2.1 一次请求到底发生了什么

前后端“连起来”的本质,就是一次HTTP通信。HTTP协议你可以理解成一个约定好的“对话规则”:客户端发送请求,服务器返回响应,双方都按这套规则来解析对方的话。

一次最普通的HTTP请求包含几个关键要素:

  • 请求方法:比如GET表示“我要拿数据”,POST表示“我要提交数据”
  • URL:告诉我请求发到哪台机器、哪个端口、哪个路径
  • 请求头:携带一些附加信息,比如Content-Type声明了请求体格式
  • 请求体:POST请求时携带的实际数据内容

服务器收到后,也会返回若干要素:

  • 状态码:比如200代表成功、404代表找不到、500代表服务器内部错误
  • 响应头:声明返回数据的格式、长度等
  • 响应体:实际的数据内容

这个对话过程,有点像你到餐厅点餐:你举起手(发起请求)告诉服务员“菜单编号A1”(URL和方法),服务员记录下你的要求(请求体),后厨做好菜之后按同样的编号端上来(响应),你看到菜就开吃(前端渲染)。

在demo里,前端发的URL是http://127.0.0.1:8000/api/say,路径/api/say就是后端的“点餐编号”,后端根据这个路径判断该返回什么内容给你。

2.2 GET和POST:两种最常见的“说话方式”

在我们的demo里,我会同时演示GET和POST两种请求,因为它们在实现细节上的差异非常能体现HTTP协议的运作方式。

请求方式业务场景数据放在哪特点
GET读取数据URL的查询参数里简单直接,可被浏览器直接访问,但不应放敏感信息
POST提交数据请求体里数据不会出现在地址栏,适合表单提交等场景

对应到代码上,后端的处理函数会不一样。在Python的http.server里,do_GET和do_POST是两个不同的方法,这点很容易理解:服务员听到的点单方式不一样,后厨的处理流程自然也不一样。

我在实际教学中经常碰到这样一个问题:有人用fetch发POST请求,但后端接口实际上是GET协议,结果前端代码看起来没问题,后端却始终返回404或405。这说明,前后端连通的第一个条件,就是方法和路径都对得上。

2.3 同源策略与跨域:为什么端口不同也算不同源

原生实现里最容易碰到的报错就是跨域。很多初学者第一次看到CORS错误时会一头雾水:“我明明请求的是本地的另一个端口,为什么也跨域了?”

这里的关键在于浏览器的安全模型——同源策略。它规定:如果一个页面里的JavaScript代码发起了网络请求,那么这个请求的地址必须和当前页面所在的“源”一致。这个“源”由三部分决定:协议、域名、端口。任何一个不一样,都算跨源。

比如我的demo里:

  • 前端页面运行在http://127.0.0.1:8080/(用静态文件服务启动)
  • 后端接口运行在http://127.0.0.1:8000/

协议相同、域名相同,但端口不同。这样浏览器的同源策略就会阻止前端直接读取后端返回的数据,除非后端在响应头里明确声明“我允许你去读”。

这就是跨域(CORS)问题的本质。框架里的各种跨域配置,本质上都是在帮你设置响应头。我们在原生实现里会手动加两行响应头,你就能清楚地看到“跨域是怎么被解决的”这件事到底做了什么。

2.4 JSON:前后端通用的“数据交换方言”

前端和后端是两个完全不同的运行环境,一个跑在浏览器里,一个跑在服务器进程里。它们之间传递数据,需要一种双方都能读懂的数据格式。JSON就是今天最通用的选择。

JSON的本质是一个字符串,但格式规则定义得很严格:键名必须用双引号、字符串值必须用双引号、不能有注释、不能有尾逗号。它和JavaScript对象长得非常像,但它们是不同的东西——前者是文本协议里的数据表示,后者是内存里的数据结构。

在demo里:

  • 前端通过JSON.stringify把对象转成JSON字符串,放进请求体。
  • 后端通过json.loads把收到的JSON字符串解析成Python字典。
  • 后端返回时又用json.dumps把字典转回JSON字符串。
  • 前端再用response.json()把它解析成JavaScript对象。

这套“序列化-传输-反序列化”的过程,在真实项目里往往被框架封装成一行代码,但它的底层逻辑就是demo里这样。如果你明白了这个往返过程,再看框架里的自动序列化配置,就不会再用得云里雾里了。

3. 环境准备与项目结构设计

3.1 工具清单(两手空空就能开始)

这个demo不需要复杂的开发环境,我用到的工具连一台最低配置的笔记本都能轻松跑起来:

  • Python 3.x(建议3.8以上,自带http.server模块)
  • 现代浏览器(推荐用Chrome或Edge,方便用开发者工具)
  • 文本编辑器(任何能写代码的编辑器都行)

如果你电脑上本来就有Python,那么环境准备这一步就完成了。Node.js方案我也给了,如果你想用那个版本,只需要装一个Node.js即可,整个过程一样简单。

我不推荐在这样的最小demo里使用“自动重启服务”“热更新”之类的额外工具,因为小项目改动频率低,而且这些工具本身就引入了新的抽象层,反而会把问题搞复杂。

3.2 目录与端口规划

我习惯把前端和后端分开放在两个目录里,这样能帮助初学者从物理结构上理解“前端项目”和“后端项目”是两回事。

minimal-demo/ ├── frontend/ │ └── index.html └── backend/ └── server.py

目录结构非常简单,但暗含着一条理念:前端代码和后端代码是独立部署的两套东西,可以不在同一台机器上。只是因为这是本机demo,才把它们放在同一个文件夹下方便管理。

端口规划也提前做好:

作用端口说明
前端静态页面8080用python -m http.server 8080启动
后端API服务8000用python server.py启动

选择8080和8000只是为了好记。在真实项目中,前端可能跑在域名解析后的80端口,后端可能跑在一个内网端口上,并通过网关转发。但对demo来说,清晰是最重要的。

3.3 一个很容易犯的错误:直接双击打开HTML

我见过太多初学者在这一步就直接双击index.html打开页面,然后在点击按钮后看到一堆“failed to fetch”报错。这背后的原因是:通过文件管理器打开的网页,其协议是file://,不是http://。

在file://协议下,页面的“源”变得非常特殊,而fetch请求又是在这种协议下最容易触发一系列限制。更关键的是:直接用文件方式打开页面,不符合真实的前端运行场景。真实世界里,前端文件一定是通过HTTP服务提供给浏览器的。

所以我在设计demo时特意要求,前端也要用python -m http.server 8080启动一个静态文件服务。这样前端是通过http://127.0.0.1:8080/index.html访问的,更接近真实项目的运行方式,也可以顺便排除掉很多奇怪的协议问题。

4. 最小demo完整实现与逐行讲解

4.1 前端页面:原生HTML + 原生fetch

前端代码放在frontend/index.html里。我先写一个简洁的界面:一个输入框、一个按钮、一个结果展示区。页面结构不复杂,重点看JavaScript里的逻辑。

<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>前后端连接最小Demo</title> <style> body { font-family: "Microsoft YaHei", Arial, sans-serif; max-width: 640px; margin: 40px auto; padding: 0 20px; line-height: 1.8; } input, button { font-size: 16px; padding: 6px 12px; } #result { margin-top: 20px; padding: 12px; background: #f5f5f5; border-radius: 6px; word-break: break-all; } </style> </head> <body> <h1>前后端连接最小实操 Demo</h1> <p>输入你的名字,点击按钮,后端会返回一段问候语。</p> <input type="text" id="name" placeholder="输入名字" /> <button id="btn">发送请求</button> <div id="result">结果会显示在这里</div> <script> const btn = document.getElementById('btn'); const resultDiv = document.getElementById('result'); const nameInput = document.getElementById('name'); async function handleSend() { const name = nameInput.value.trim(); if (!name) { resultDiv.textContent = '请输入名字后再发送。'; return; } resultDiv.textContent = '请求发送中...'; try { const response = await fetch('/api/say', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: name }) }); if (!response.ok) { throw new Error('后端返回状态码: ' + response.status); } const data = await response.json(); resultDiv.textContent = '后端回复:' + data.message; } catch (error) { resultDiv.textContent = '请求失败:' + error.message; console.error('fetch error:', error); } } btn.addEventListener('click', handleSend); </script> </body> </html>

这里要特别解释一个初学者容易懵的点:fetch('/api/say', ...)里的URL为什么不是http://127.0.0.1:8000/api/say?

因为/api/say是以斜杠开头的绝对路径写法,它表示“相对于当前站点根路径”。当前页面运行在http://127.0.0.1:8080/index.html,所以/api/say会被理解为http://127.0.0.1:8080/api/say。也就是说,这个请求发到了前端的8080端口,而后端监听的是8000端口,两者对不上。

所以在真实演示中,我会在JavaScript里把URL明确写为:

const API_BASE = 'http://127.0.0.1:8000'; const response = await fetch(API_BASE + '/api/say', { ... });

这样前端就知道请求要跨越端口,发到8000去。跨域问题也就从这里开始埋下伏笔。

4.2 后端服务:用Python标准库手写路由与响应

后端代码放在backend/server.py里。我不引入任何第三方依赖,只用标准库的http.server来实现。

# backend/server.py from http.server import BaseHTTPRequestHandler, HTTPServer import json class Handler(BaseHTTPRequestHandler): # 统一设置跨域响应头和通用响应头 def _set_cors_headers(self): self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') self.send_header('Access-Control-Max-Age', '86400') def do_OPTIONS(self): # 浏览器在跨域POST前会先发一个预检请求,这里直接放行 self.send_response(204) self._set_cors_headers() self.end_headers() def _send_json(self, data, status=200): body = json.dumps(data, ensure_ascii=False).encode('utf-8') self.send_response(status) self.send_header('Content-Type', 'application/json; charset=utf-8') self.send_header('Content-Length', str(len(body))) self._set_cors_headers() self.end_headers() self.wfile.write(body) def do_GET(self): if self.path == '/api/health': self._send_json({'status': 'ok', 'message': '后端服务运行中'}) else: self._send_json({'error': '路径不存在'}, status=404) def do_POST(self): if self.path != '/api/say': self._send_json({'error': '路径不存在'}, status=404) return # 读取请求体 content_length = int(self.headers.get('Content-Length', 0)) raw_body = self.rfile.read(content_length) try: data = json.loads(raw_body.decode('utf-8')) name = data.get('name', '') except json.JSONDecodeError: self._send_json({'error': 'JSON格式错误'}, status=400) return if not name: self._send_json({'error': 'name不能为空'}, status=400) return # 模拟业务处理:生成返回消息 message = '你好,' + name + '!前端请求已成功到达后端。' self._send_json({'message': message}) def main(): server = HTTPServer(('127.0.0.1', 8000), Handler) print('后端服务已启动: http://127.0.0.1:8000') print('按 Ctrl+C 停止服务') server.serve_forever() if __name__ == '__main__': main()

这段代码虽然短,但包含了后端HTTP处理的核心要素,我逐一说明。

先看do_GET和do_POST方法。它们对应HTTP请求方法。当浏览器发来GET请求时,BaseHTTPRequestHandler会自动调用do_GET,发来POST时调用do_POST。所以你的后端逻辑本质上就是在回答这个类的一个问题:某某请求来了,你回什么?

再看自定义的_send_json方法。我在这里把复杂的响应逻辑封装成一行,它做的事情其实就是三件:

  • 调用json.dumps把Python字典转成JSON字符串
  • 设置Content-Type: application/json,告诉浏览器响应体是什么格式
  • 设置Content-Length,告知响应体长度,让浏览器知道什么时候可以读完数据

再解释一下do_POST里的请求体读取逻辑:

content_length = int(self.headers.get('Content-Length', 0)) raw_body = self.rfile.read(content_length)

这可能是新手最难理解的一段。HTTP请求到达服务器后,请求体的长度是放在请求头里的,字段叫Content-Length。服务器要读取请求体,得先知道数据有多长,所以先拿到这个数字,再去读对应的字节数。

这个细节在框架里会被封装的完全看不见,但它是HTTP协议一个非常典型的设计:所有信息都要在约定的位置告诉对方,对方才能正确处理。就像你寄快递,必须在面单上写明“内件数量”一样。

4.3 启动前后端并完成一次完整交互

后端启动很简单:

cd backend python server.py

看到终端输出“后端服务已启动”后,不要关掉这个终端。它要一直驻留在那里监听请求。

然后另开一个终端,启动前端静态文件服务:

cd frontend python -m http.server 8080

接下来在浏览器访问http://127.0.0.1:8080/index.html,输入任意名字,点击发送按钮。

正常情况下你会看到:

  • 后端终端输出一行访问日志,类似127.0.0.1 - - [时间] "POST /api/say HTTP/1.1" 200
  • 前端页面的结果区域显示“后端回复:你好,XXX!前端请求已成功到达后端。”
  • 浏览器开发者工具里的Network面板能看到这个请求状态码是200,响应体是一段JSON

这个过程跑通了,你就完成了人生中第一次真正意义上“前后端连接”的全链路操作。

4.4 网络上为什么能看到完整的往返过程

为了让你看得更清楚,我建议在浏览器里按F12打开开发者工具,切到Network(网络)面板,再重复点击一次按钮。

你会看到一条名为/api/say的记录。点开它可以看到:

  • General区域:显示请求URL和请求方法(POST)
  • Request Headers区域:能看到Content-Type为application/json,这是你在前端手动设置的
  • Response区域:显示后端返回的JSON字符串
  • Timing区域:显示请求耗时

对应到后端终端日志,你还能看到服务器收到了完全相同的方法和路径。这个过程非常直观地回答了“前后端怎么连起来的”——它们通过HTTP协议、在约定的端口上、以约定的路径和格式,完成了一次数据的双向传输。

有一点值得强调:这个demo中,后端终端打印的访问日志是http.server模块自带的,在真实项目的框架里也有类似的日志能力,只是输出格式和详细程度不同。你在定位“请求到底有没有到达后端”这个问题时,第一件事就是看后端日志,而不是去看前端代码。

4.5 对照实现:Node.js原生http模块版

如果你的电脑没有Python,或者你对JavaScript更熟,用Node.js也能做同样的事。核心代码如下:

// backend-server.js const http = require('http'); const server = http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } if (req.method === 'POST' && req.url === '/api/say') { let raw = ''; req.on('data', chunk => raw += chunk); req.on('end', () => { try { const data = JSON.parse(raw); const name = data.name || ''; const message = '你好,' + name + '!Node.js后端已收到请求。'; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message })); } catch (err) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'JSON格式错误' })); } }); return; } if (req.method === 'GET' && req.url === '/api/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok' })); return; } res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: '路径不存在' })); }); server.listen(8000, () => { console.log('后端服务已启动: http://127.0.0.1:8000'); });

Node版直接把Content-Length和响应头设置都封装在res对象的内部机制里,所以代码看起来比Python标准库版本更简洁。但本质上,它处理的还是同一套HTTP规则:读请求、看路径、设置状态码、设置响应头、写响应体。

你可以两个版本的代码都跑一遍,感受一下不同语言在HTTP处理上的差异——你会发现,语言虽然不同,但HTTP协议的基本流程是完全一致的。这正是学习原生的价值:你学会的是语言无关的原理,而不是某个框架的固定用法。

5. 常见问题与排查技巧实录

5.1 跨域报错:前端请求到达了后端,但浏览器不让你读

这个报错恐怕是初学者遇到最多的问题。表现形式是在浏览器控制台看到:

Access to fetch at 'http://127.0.0.1:8000/api/say' from origin 'http://127.0.0.1:8080' has been blocked by CORS policy

看到这行字,请先理解现状:你的请求其实已经成功到达后端了,后端也已经返回了数据,但浏览器在把数据交给JavaScript代码之前,发现响应头里没有允许跨域的声明,于是帮你拦截了下来。

解决办法很简单,在后端响应头里加上Access-Control-Allow-Origin即可。我的demo里已经加好了。如果要限制只允许某个源请求,可以把*换成具体的地址,比如http://127.0.0.1:8080。

我在排障时有个习惯:如果临时忘了加响应头,会先用curl直接请求后端地址,绕过浏览器这个环节,确认后端本身没有问题:

curl -X POST http://127.0.0.1:8000/api/say \ -H "Content-Type: application/json" \ -d '{"name":"test"}'

如果curl能正常返回JSON,而浏览器报错,那问题就锁定在CORS响应头缺失上,不用瞎翻前端代码了。

5.2 前端响应正常,但data.message是undefined

另一个高频问题是:Network面板里明明看到请求成功,返回状态码也是200,但前端却取不到data.message。

这个问题的原因往往有两个。

第一,后端返回的响应体不是JSON格式。可能是某个错误信息把响应体变成了普通文本,比如后端崩了返回了HTML格式的错误页。你可以看一眼Network面板里Response选项卡的内容,如果看到的不是JSON结构,就说明后端返回格式不符合预期。

第二,前端代码没有调用response.json()。如果只用了response.text(),拿到的就是一个字符串,自然取不到.message属性。这种情况在真实项目中很常见,要养成“后端返回什么格式,前端就用什么方式解析”的习惯。

5.3 修改了后端代码,但行为没变化

初学者在改完Python代码后,经常发现请求结果还是旧的。这个问题的根源在于:http.server不会在代码变更后自动重启服务。

你启动一次python server.py,它就一直在那个老进程里运行。改完代码后,如果不在终端里按Ctrl+C停掉进程,再重新执行python server.py,那么你发出的所有请求仍然都由旧代码处理。

这是一个很低级却非常普遍的坑。同样的问题在Node版的npm run dev模式下不常见,因为很多启动命令带了自动重启功能,但原生方案没有这个魔法。建议把“改完代码→重启服务→再看结果”这串动作刻在肌肉记忆里。

5.4 端口被占用:Address already in use

如果你在启动时看到类似OSError: [Errno 98] Address already in use(Linux/Mac)或只能绑定到该端口(Windows),说明8000或8080端口被其他程序占用了。

解决办法有三条路:

  • 查出占用端口的进程并停掉它
  • 换一个端口,比如http.server 8081或让后端监听8001,但记得同步修改前端fetch的地址
  • 重启电脑这类“暴力方案”不建议,容易误伤其他服务

我在做这个demo教学时,经常建议学员直接用不太常用的端口,比如8090,避免和本地已有的开发服务冲突。

5.5 网络面板看不到任何请求

如果你点击按钮后毫无反应,Network面板里连一条请求记录都没有,问题就出在“请求根本没发出去”。

排查顺序一般是:

  • 按钮的点击事件有没有绑定成功(在addEventListener那行加个console.log试试)
  • fetch代码有没有语法错误(浏览器控制台有没有红色报错)
  • fetch的URL是不是写成了相对路径导致发到了错误端口(这个我也在前面踩过)

这里我再强调一次绝对路径和相对路径的差别。你在页面上写fetch('/api/say'),浏览器会把请求发到当前页面的端口,也就是8080。这个请求会被前端静态文件服务收到,但它显然不认识/api/say这个路径,就会返回一个HTML格式的404页面。于是你在Network里看到的不是后端API的200,而是一个前端静态服务器的404。要避免这个问题,最简单的做法是把请求地址写全:

const API_BASE = 'http://127.0.0.1:8000'; fetch(API_BASE + '/api/say', ...)

这个细节撑起了整个demo里一半的调试时间,也帮很多初学者建立了“前端、后端分别占用独立端口”的空间感。

这个demo跑通之后,你还可以做什么

其实这个demo跑通之后,你已经掌握了前后端连接的完整心智模型。我自己在实际带人时还会做两个扩展练习,都花不了几分钟,但效果很好。

第一个练习是把后端返回的内容改成本地文件里的内容,比如读取一个文本文件并把内容返回给前端。这会让你进一步理解:后端不只可以返回写死的JSON,还可以动态生成数据,而这正是后端服务的价值所在。

第二个练习是改一下前端代码,用两个按钮分别发送GET和POST请求,观察Network面板里两者的差异。你会发现GET请求的地址会带着查询参数,POST请求的数据在请求体里,两者在协议层面是完全不同的“说话方式”。

做过这两个小练习之后,再回去用任何一个前后端框架,你会发现很多配置选项不再那么抽象了:前端代理解决的就是端口不同导致的跨域问题,后端路由映射解决的就是路径匹配问题,序列化配置解决的就是数据格式转换问题。所有高级工具,本质上都是在这个最小模型之上盖的房子。

这套原生demo我至少给三批新人演示过,每次都有人在看到后端终端跳出访问日志时发出“原来如此”的感叹。前后端从来不是两个隔海相望的黑盒,它们之间流动的,始终不过是那一句又一句按HTTP规范组织起来的明文数据罢了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询