背景

想开发一个系统学习玩玩,因为现在前后端分离的优势,所以想用前后端分离的方式进行开发。之前我工作中用过Vue,于是选了个Vue的后台管理前端项目(vue-admin-template),我比较喜欢Python,之前用过Django觉得太重,对比了Sanic和Flask最终选用了FastAPI作为后台框架,使用Nginx作为反向代理。

有以下几点需要注意:

  • 我的域名是"fucaijin.cn",本文中均使用"域名"来替代。
  • 我使用的是阿里云的服务器,系统是Debian10.10。
  • 我电脑是Windows系统,先在本地开发,再部署到阿里云服务器

因为怕小白看不懂本文,所以我写的尽量啰嗦一点,比较很多细节如果不说明,跟着操作可能容易被各种问题卡住。

工作流程

  1. 访问https://域名/vue-admin-template时访问前端项目(vue-admin-template)
  2. 如果之前没有登录过,会打开浏览页面,然后输入用户名和密码登录,登录接口会请求https://域名/prod-api/vue-admin-template/user/login,(FastAPI会收到这个请求并响应)

前置知识要求

要看懂本文,需要掌握以下知识

  • Vue2以及Vue-cli、WebStormIDE的使用
  • Nginx的安装和配置
  • Linux的常用命令及shell脚本的编写
  • Python基础知识、pip的使用、虚拟环境的使用,及其FastAPI框架的使用

vue-admin-template+Nginx

  1. 下载vue-admin-template: git clone https://github.com/PanJiaChen/vue-admin-template.git
  2. 使用WebStorm打开项目,执行npm install
  3. 下载完所有依赖包后,执行vue-cli-service serve或npm的快捷方式dev,如果项目启动起来了,说明前端项目没有问题
  4. 修改main.js,注释if (process.env.NODE_ENV === 'production') {const { mockXHR } = require('../mock'); mockXHR();},取消:在正式环境时拦截request请求使用mock模拟数据
  5. 因为我的域名根路径关联了别的项目,我想在访问https://fuciajin.cn/vue-admin-template时才访问本项目,所以这里我需要修改vue.config.js,将publicPath的值由"/"改为"/vue-admin-template"。(如果需要访问https://域名/xxx时访问本项目,就把vue.config.js的publicPath的值改为xxx)
  6. 执行vue-cli-service build或npm的快捷方式build:prod编译打包到dist这个目录下(打包路径可以在vue.config.js的outputDir属性修改)
  7. 打包完成项目后,将项目上传到服务器,比如我将打包好后dist内的所有文件上传到我阿里云的路径/home/fucaijin/project/vue_project/dist
  8. 配置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配置。
  9. 在浏览器访问https://域名/vue-admin-template测试前端项目是否部署成功
  10. 前端项目部署完成

FastAPI+Nignx

  1. 创建目录D:\FCJ\Projects\PycharmProjects\FastApi作为FastAPI的项目根目录,FastApi是项目名称,取什么名称任意,本章节该目录为项目目录。
  2. 创建python虚拟环境: cd到项目目录,执行virtualenv -p python3 env命令使用Pyhon3生成一个虚拟环境,该环境名为env,该命令执行完成后会生成一个venv的目录,执行source env/bin/activate即可激活虚拟环境,然后pip的包都会在该虚拟环境中安装。
  3. 创建文件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
  1. 执行pip install -r requirements.txt安装上一步创建的文件中的包
  2. 在项目目录创建文件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)
  1. 本地测试项目是否能跑通,cd到项目目录,执行venv/Scripts/python run.py,项目就会跑起来了,访问127.0.0.1:8000/docs127.0.0.1:8000/hello,如果都能打开页面说明项目已经正常运行了
  2. 在服务器创建目录/home/fucaijin/project/fastApiProject/FastApi作为服务端的FastAPI项目的目录,把本地的项目目录中的run.py和requirements.txt文件都上传到该目录中
  3. 在服务器端的FastAPI项目目录中执行virtualenv -p /usr/bin/python3 env使用Pyhon3生成一个虚拟环境
  4. 配置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配置。
  5. 在服务器端的FastAPI项目目录中执行env/bin/python3 run.py,即可在服务端运行FastAPI项目,此时在浏览器访问https://域名/prod-api/vue-admin-template/hello,就会访问到hello接口,不过如果断开服务器连接或者Ctrl+C会停止该项目的运行,下面写一个脚本来启动停止该项目
  6. 在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
  1. 到这里就完成了使用Nginx来代理请求转发到FastAPI这个项目

Q.E.D.


做一个热爱生活的人