前后端分离的 Web App 踩坑笔记
2020-04-26 04:49:46
This post is also available in English and alternative languages.
最近在做一个整整 10 学分的团队课设。
Environment
- 前端:React + Antd
- 后端:PHP + MySQL
- 刚开始技术选型时准备采用 Flask 作为后端架构,毕竟半年前大家刚做了 Flask 的单人项目,对于其他组员来说 Python 比 PHP 更容易上手一些。但是在 Python 中缩进对代码逻辑起到影响,如果编码过程或者冲突处理时没做好,可能会产生比较棘手的麻烦。最终还是选了 PHP,代价是增加了约一个月的学习成本,不过整体代码可读性和维护性都高了不少,依赖注入也很香。
- Jetbrains 全家桶自带 deployment 功能,可以快捷部署当前文件至 remote 以便测试。但是全量部署更适合 Github Actions。配置好
appleboy/scp-action@master
,就能将 Node.js 项目的dist
目录打包后再上传至生产环境,节约了不少时间。值得注意的是,当 source path 填写为dist/
时,文件将被部署到远程目录的path/dist/
下。在scp
中可以使用dist/*
来解决这个问题,在此插件中可以指定strip_components: 1
来去除前导路径元素(推荐),或者在 nginx 中修改根目录至wwwroot/dist
。不建议修改 nginx 的wwwroot
配置,这将导致 lnmp 无法更新 Let’s Encrypt 证书(也可以在 nginx 中为.well-known
路径单独指定根目录)。 - 前后端跨域是非常棘手的一个问题,我们在此花了数十个小时 debug。CORS 的出现保护了用户免遭跨站攻击,但是也为前后端对接产生了巨大的麻烦。
- 当用到跨域 API 调用,通常做法是在后端 header 加上
Access-Control-Allow-Origin: *
,但 Fetch 请求不允许这种掩耳盗铃的操作。解决办法是动态指定参数或者将前端域名写死。 - CORS 预检请求方法是
OPTIONS
,因此需要设置Access-Control-Allow-Methods: POST, GET, OPTIONS
。根据 Issue #251 · whatwg/fetch 的讨论中,当 Fetch 的withCredentials = true
时(跨域请求携带目标域的 Cookies),此处不允许通配符,因此应当避免使用Access-Control-Allow-Methods: *
。此规则同样也适用于Access-Control-Allow-Methods
,因此应当手动声明后端允许的 request header,例如form-data
。 - 必须设定
Access-Control-Allow-Credentials: true
,才能使后端接受前端跨域发送的 Cookies。 - 我们使用 PHPSESSION 来鉴权,但 Fetch 无法响应 response header 中的
set-cookie
(并非http-only
),导致浏览器无法存储用户凭据。前端同学试了 axios 和 Webpack Proxy 也没有什么头绪。对此有两个临时解决方案:- 将
sessionId
直接塞在登录接口的data
字段里回传给前端,然后手动设置 cookie(或者存储至 SessionStorage 中)。 - 让前端通过 XMLHttpRequest 发送原生的
POST
的请求,浏览器自己会响应set-cookie
的字段。
- 将
- 当用到跨域 API 调用,通常做法是在后端 header 加上
- 自 MySQL 5.7 开始,正式引入了 JSON 作为字段类型。这意味着我们再也不用为同质化的数据表手写烦人的 SQL 文档了,只需要将他们抽象成一种数据,然后前后端再根据 JSON 中的某个字段来路由逻辑即可。