背景
想开发一个系统学习玩玩,因为现在前后端分离的优势,所以想用前后端分离的方式进行开发。之前我工作中用过Vue,于是选了个Vue的后台管理前端项目(vue-admin-template),我比较喜欢Python,之前用过Django觉得太重,对比了Sanic和Flask最终选用了FastAPI作为后台框架,使用Nginx作为反向代理。
有以下几点需要注意:
- 我的域名是"fucaijin.cn",本文中均使用"域名"来替代。
- 我使用的是阿里云的服务器,系统是Debian10.10。
- 我电脑是Windows系统,先在本地开发,再部署到阿里云服务器
因为怕小白看不懂本文,所以我写的尽量啰嗦一点,比较很多细节如果不说明,跟着操作可能容易被各种问题卡住。
工作流程
- 访问
https://域名/vue-admin-template
时访问前端项目(vue-admin-template) - 如果之前没有登录过,会打开浏览页面,然后输入用户名和密码登录,登录接口会请求
https://域名/prod-api/vue-admin-template/user/login
,(FastAPI会收到这个请求并响应)
前置知识要求
要看懂本文,需要掌握以下知识
- Vue2以及Vue-cli、WebStormIDE的使用
- Nginx的安装和配置
- Linux的常用命令及shell脚本的编写
- Python基础知识、pip的使用、虚拟环境的使用,及其FastAPI框架的使用
vue-admin-template+Nginx
- 下载vue-admin-template:
git clone https://github.com/PanJiaChen/vue-admin-template.git
- 使用WebStorm打开项目,执行
npm install
- 下载完所有依赖包后,执行
vue-cli-service serve
或npm的快捷方式dev,如果项目启动起来了,说明前端项目没有问题 - 修改main.js,注释
if (process.env.NODE_ENV === 'production') {const { mockXHR } = require('../mock'); mockXHR();}
,取消:在正式环境时拦截request请求使用mock模拟数据 - 因为我的域名根路径关联了别的项目,我想在访问
https://fuciajin.cn/vue-admin-template
时才访问本项目,所以这里我需要修改vue.config.js,将publicPath的值由"/"改为"/vue-admin-template"。(如果需要访问https://域名/xxx
时访问本项目,就把vue.config.js的publicPath的值改为xxx) - 执行
vue-cli-service build
或npm的快捷方式build:prod编译打包到dist这个目录下(打包路径可以在vue.config.js的outputDir属性修改) - 打包完成项目后,将项目上传到服务器,比如我将打包好后dist内的所有文件上传到我阿里云的路径
/home/fucaijin/project/vue_project/dist
- 配置Nginx,
vi /etc/nginx/nginx.conf
,在server节点内添加内容location /vue-admin-template { alias /home/fucaijin/project/vue_project/dist;}
,意思是访问https://域名/vue-admin-template
时去/home/fucaijin/project/vue_project/dist
这个目录寻找资源,寻找了index.html就返回该文件。修改完成后执行nginx -t
测试Nginx的配置文件是否正确,如果有successful
字样,再执行nginx -s reload
重新加载Nginx配置。 - 在浏览器访问
https://域名/vue-admin-template
测试前端项目是否部署成功 - 前端项目部署完成
FastAPI+Nignx
- 创建目录
D:\FCJ\Projects\PycharmProjects\FastApi
作为FastAPI的项目根目录,FastApi是项目名称,取什么名称任意,本章节该目录为项目目录。 - 创建python虚拟环境: cd到项目目录,执行
virtualenv -p python3 env
命令使用Pyhon3生成一个虚拟环境,该环境名为env,该命令执行完成后会生成一个venv的目录,执行source env/bin/activate
即可激活虚拟环境,然后pip的包都会在该虚拟环境中安装。 - 创建文件requirements.txt,内容如下
aiofiles==0.6.0
atomicwrites==1.4.0
attrs==20.3.0
bcrypt==3.2.0
certifi==2020.12.5
cffi==1.14.4
chardet==4.0.0
click==7.1.2
colorama==0.4.4
cryptography==3.3.1
dnspython==2.0.0
ecdsa==0.14.1
email-validator==1.1.2
fastapi==0.63.0
h11==0.11.0
idna==2.10
importlib-metadata==3.3.0
iniconfig==1.1.1
Jinja2==2.11.2
MarkupSafe==1.1.1
packaging==20.8
passlib==1.7.4
pluggy==0.13.1
py==1.10.0
pyasn1==0.4.8
pycparser==2.20
pydantic==1.7.3
pyparsing==2.4.7
pytest==6.2.1
python-jose==3.2.0
python-multipart==0.0.5
requests==2.25.1
rsa==4.6
six==1.15.0
SQLAlchemy==1.3.22
starlette==0.13.6
toml==0.10.2
typing-extensions==3.7.4.3
urllib3==1.26.2
uvicorn==0.13.2
zipp==3.4.0
- 执行
pip install -r requirements.txt
安装上一步创建的文件中的包 - 在项目目录创建文件run.py(文件名随意)
#!/usr/bin/python3
# -*- coding:utf-8 -*-
# __author__ = '__Jack__'
from typing import Optional
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi import APIRouter, status, Form, File, UploadFile, HTTPException, Body, Query, Cookie, Request, Depends
app = FastAPI() # 这里的变量名不一定是app,随意自定义即可
# 这里定义一个函数作为依赖,供下面的接口去使用
async def verify_token(request: Request):
token = request.headers.get("X-Token")
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="token 不正确"
)
# 假设到这一步,进行验证、解析token,假设解析出user_id···
user_id = 'fcj'
return user_id
@app.get('/')
async def root():
return {'hello': 'world'}
@app.get('/hello')
async def hello():
return {'hello': 'world'}
@app.post("/user/login")
async def login(password=Body(None), username=Body(None)): # 定义表单参数,如果是提交的表单而不是json(即请求头是Content-Type: application/x-www-form-urlencoded,不是"Content-Type: application/json;charset=UTF-8",必须使用Form(...)接收
"""
登录
"""
# 查询用户账号、密码、(角色)权限、昵称、头像,如果查询成功就生成token(账号密码+时间)并更新数据库中的token,然后返回token、密码、(角色)权限、昵称、头像
print(f"{username}\t-\t{password}")
return {"code":20000,"data":{"username": username, 'token': 'admin-token'}}
@app.get('/user/info')
async def get_user_info(user_id: str = Depends(verify_token)):
# 通过user_id获取用户信息
"""
测试接口,通过user_id获取用户信息
"""
print(f"user_id={user_id}")
return {"code":20000,"data":{"roles":["admin"],"introduction":"I am a super administrator","avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif","name":"Super Admin"}}
@app.post("/user/logout")
async def logout(password=Body(None), username=Body(None)): # 定义表单参数,如果是提交的表单而不是json(即请求头是Content-Type: application/x-www-form-urlencoded,不是"Content-Type: application/json;charset=UTF-8",必须使用Form(...)接收
"""退出登录"""
print(f"{username}\t-\t{password}")
return {"code":20000,"data":{"username": username, "password": password, 'token': 'admin-token'}}
# 启动命令(uvicorn py文件名:FastAPI实例的变量名):uvicorn run:app --reload # 如果使用--reload参数启动,可以实现只要代码有更改,就重启。
# 访问接口文档(127.0.0.1:8000/docs)测试请求
if __name__ == '__main__':
uvicorn.run('run:app', host='127.0.0.1', port=8000, reload=True, debug=True, workers=1)
- 本地测试项目是否能跑通,cd到项目目录,执行
venv/Scripts/python run.py
,项目就会跑起来了,访问127.0.0.1:8000/docs
和127.0.0.1:8000/hello
,如果都能打开页面说明项目已经正常运行了 - 在服务器创建目录
/home/fucaijin/project/fastApiProject/FastApi
作为服务端的FastAPI项目的目录,把本地的项目目录中的run.py和requirements.txt文件都上传到该目录中 - 在服务器端的FastAPI项目目录中执行
virtualenv -p /usr/bin/python3 env
使用Pyhon3生成一个虚拟环境 - 配置Nginx访问FastAPI项目:
vi /etc/nginx/nginx.conf
,在server节点内添加内容location /prod-api/vue-admin-template/ { proxy_pass http://127.0.0.1:8000/;}
,让nginx收到请求https://域名/prod-api/vue-admin-template/xxx/...
时,将请求转发到http://127.0.0.1:8000/xxx/...
,就能访问到FastAPI的项目接口了。修改完成后执行nginx -t
测试Nginx的配置文件是否正确,如果有successful
字样,再执行nginx -s reload
重新加载Nginx配置。 - 在服务器端的FastAPI项目目录中执行
env/bin/python3 run.py
,即可在服务端运行FastAPI项目,此时在浏览器访问https://域名/prod-api/vue-admin-template/hello
,就会访问到hello
接口,不过如果断开服务器连接或者Ctrl+C会停止该项目的运行,下面写一个脚本来启动停止该项目 - 在FastAPI项目目录中执行
vi nohup_fastapi.sh
,内容如下,然后将该文件保存。该文件的意思是,在该文件所在的目录执行./nohup_fastapi.sh start run.py
或./nohup_fastapi.sh run run.py
时就会在后台执行run.py这个文件,这样就算断开服务器连接也不怕FastAPI项目关闭了。执行./nohup_fastapi.sh stop run.py
或./nohup_fastapi.sh kill run.py
就停掉FastAPI项目。
#!/bin/bash
if [[ $1 = 'start' ]] || [[ $1 = 'run' ]]; then
# nohup python $2 > ${2/'.py'/'.log'} 2>&1 &
nohup env/bin/python3 $2 > /dev/null 2>&1 &
echo "start:" $2
elif [[ $1 = 'stop' ]] || [[ $1 = 'kill' ]]; then
ps -ef | grep env/bin/python3 | grep -v grep | awk '{print $2}' | xargs kill -9;
echo "stop!" $2
else
echo "nothing_run"
fi
- 到这里就完成了使用Nginx来代理请求转发到FastAPI这个项目
Q.E.D.