Appearance
前端和后端
打开一个网站,屏幕上看到的所有东西——按钮、表格、图表、动画——都属于前端;但保存数据、查询列表、做计算这些"重活",前端干不了,要交给后端。前端和后端是两套完全不同的代码,跑在不同的地方,通过 HTTP 接口通信。
排查问题的时候,前端后端是两套完全不同的思路:页面点了没反应,先看浏览器控制台有没有报错、Network 面板里请求发出去没有;接口 500,直接去后端日志里捞对应的 trace id;接口明明返回 200 但页面数据是空的,大概率是字段名前后端没对齐——这种情况最迷惑,因为看起来"没错"但功能就是坏的。
一、前端
前端说白了就是浏览器里跑的那部分。涉及的技术就三种,各管一摊:
- HTML 管页面结构——页面上有哪些元素、元素之间什么层级关系
- CSS 管样式——颜色、字体、布局、动画、响应式
- JavaScript 管交互——点击事件、表单提交、调接口、动态更新 DOM
举个例子,页面上一个"删除"按钮能正常工作,背后要满足几件事:HTML 里有这个按钮元素,CSS 没把它 display:none 隐藏掉,JavaScript 给它绑了点击事件,点击触发的接口请求正常返回,返回之后 JS 还要把这行数据从表格里移除。任何一步断了,用户看到的现象都是"点了没反应"。
前端源码写完之后要打包——React/Vue 的 JSX/Vue 文件不能直接跑,要先编译成普通 JS,CSS 也要压缩,最后产出一堆静态文件:index.html 加上 assets/ 下面的 JS、CSS、图片、字体。这些文件可以放 Nginx、对象存储(OSS/S3)或者 CDN,反正都是静态文件,谁都能托管。
二、后端
后端服务跑在服务器上(虚拟机、容器、Kubernetes Pod 都行),主要干四件事:接收 HTTP 请求、处理业务逻辑、访问数据库或者其他服务、返回结果。用什么语言写都行——Python 的 FastAPI/Django、Java 的 Spring Boot、Go 的 net/http、Node.js 的 Express——本质都是监听一个端口,等请求进来,然后处理。
后端代码里常见的几个对象:
- 路由:把 URL 路径跟处理函数对应起来,比如
/api/users走get_users()函数 - 处理函数:具体干活的代码,读参数、做业务、查数据库、返回 JSON
- 模型(Model):描述业务对象或者数据库表,比如
User对应users表 - 数据库连接:管理到 MySQL、PostgreSQL、Redis 的连接池
- 中间件:横切关注点,认证、日志、跨域(CORS)、异常处理、限流这些都属于中间件
后端程序一般监听一个端口,比如 8080。但公网用户不会直接访问这个端口——中间隔着 Nginx、负载均衡或者 API 网关。原因是直接暴露后端端口有一堆问题:没有 HTTPS 卸载、没有认证统一入口、没有限流熔断、IP 暴露有安全风险。所以正常架构里后端端口只在内部网络可达,外部通过入口层转发进来。
三、JSON
前后端分离之后,后端返回的几乎都是 JSON。一个典型的接口响应长这样:
json
{
"items": [
{"id": 1, "name": "巡检 web-01", "status": "success"},
{"id": 2, "name": "备份 mysql-01", "status": "running"}
],
"total": 2
}前端拿到之后,用 items 渲染表格,用 total 算分页。这就要求后端返回的字段名、类型、结构必须稳定——前端代码是按约定的字段名写死的,后端改一个字段名,前端这边就读不到了。
字段没对齐是个非常隐蔽的坑。比如后端返回的是 task_status,前端代码读的是 status,接口状态码 200,Network 里看响应也有数据,但页面上"状态"那一列是空的。这种问题光看监控根本发现不了,得对着响应 JSON 和前端代码逐字段比对才能定位。所以接口契约很重要,后端改字段一定要提前跟前端打招呼,或者用 GraphQL/Protobuf 这种强约束的方案。
四、前后端分离部署
把前端和后端分别部署到不同的地方,有它的道理:
浏览器第一次打开网站,先从 CDN/Nginx 拉 index.html 和 JS/CSS 文件——这些是静态资源,放 CDN 上全球加速,放 Nginx 上本地直出,反正没必要走后端。JS 跑起来之后,再去调后端 API 拿动态数据。这样一个页面可能同时访问两个域名:static.example.com(静态资源)和 api.example.com(接口),各自走各自的链路,扩展和优化都方便。
| 内容 | 通常放在哪里 |
|---|---|
| 前端静态文件 | Nginx、对象存储、CDN |
| 后端 API | 虚拟机、容器、Kubernetes |
| 数据库 | 云数据库、自建集群 |
| 入口层 | Nginx、API 网关、Ingress |
五、跨域
浏览器有个安全策略叫同源策略——如果两个 URL 的协议、域名、端口有任何一个不一样,就视为"不同源",JS 默认不能跨源读响应。比如 https://console.example.com 的页面去调 https://api.example.com 的接口,域名不一样,浏览器会按跨域处理。
跨域不是网络层的问题,是浏览器单方面加的安全限制——后端其实已经正常处理了请求、返回了数据,但浏览器看到响应头里没有 Access-Control-Allow-Origin,就直接拦住 JS 不让它读。所以跨域问题排查时,Network 里能看到请求是 200,但 JS 拿不到数据,控制台报 CORS 错。
解决办法是后端在响应里加 CORS Header,告诉浏览器"这些源是允许的"。涉及非简单请求(比如 POST JSON)时,浏览器还会先发一个 OPTIONS 预检请求问后端"我能不能发"。所以跨域排查要看的几个东西:OPTIONS 请求的响应有没有 Access-Control-Allow-Origin、允许的方法包不包含你要用的、允许的 Header 包不包含你自定义的。少一项都会被浏览器拦。
六、联调排查
把前面讲的层都串起来,联调时遇到问题根据现象定位:
| 现象 | 大概率问题在哪 |
|---|---|
| 接口返回 401 | Token 没带、Cookie 没传、登录态过期 |
| 接口返回 404 | API 路径写错、网关没转发、后端路由没注册 |
| 接口返回 500 | 后端代码异常、数据库连不上、下游服务超时 |
| 接口 200 但页面空白 | 字段名对不上、数据结构变了、前端渲染逻辑有问题 |
| 控制台 CORS 报错 | 后端跨域配置缺字段,或者 Nginx 把 CORS Header 吃掉了 |
| JS 文件 404 | 前端构建路径变了、静态资源发布到了错误目录、CDN 缓存了旧路径 |
记住一个排查顺序:先看 Network 面板(请求发出去了吗?响应是多少?),再看后端日志(请求有没有到后端?报了什么错?),最后看代码(字段对得上吗?路由配置对吗?)。这套流程跑下来,前后端联调的问题基本都能定位到具体某一层。