Git Product home page Git Product logo

fastapi-vue-template's Introduction

使用 python fastapi+vue 快速搭建网站

概述

传统网站由一个 web 框架完全承担,例如基于 nodejs 的 express,koa,基于 python 的 django,tornado。新型网站演变为用 vue 开发前端系统,使用 api 框架开发后端 api 请求接口的模式。本文就介绍一下后端使用 python 的 fastapi 框架开发 api 接口,前端使用 vue 框架开发界面的方式快速开发网站。

搭建 fastapi 服务

首先建立一个项目文件夹,并建立两个子文件夹 backend 和 frontend。后端的代码将放置在 backend 文件夹中,前端的代码放在 frontend 文件夹里。

安装 fastapi

cd backend
pip install fastapi uvicorn

编写 api 接口文件 backend/app/init.py

from typing import Optional

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Optional[str] = None):
    return {"item_id": item_id, "q": q}

运行后端服务

uvicorn app:app --reload --host 0.0.0.0

验证是否运行成功

curl http://127.0.0.1:8000/items/5?q=somequery

查看自动生成文档

http://127.0.0.1:8000/docs

搭建 vue 前端

这里我们采用了 ant design pro vue 作为我们的脚手架。

cd frontend
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git .
yarn install
yarn serve

完成后,前端系统就运行在 8000 端口了。但此时,我们看到的前端只是一个静态网站,并没有动态内容。所有和后端的交互都是通过 mock 方式模拟的。在正式环境中,这些请求需要和之前搭建的 fastapi 后端交互完成。

添加 hello world 页面

在 frontend/src/views/helloworld 目录下,加入我们的页面 HelloWorld.vue

<template>
  <div>
    <h1>hello world</h1>
  </div>
</template>

将 hello 页面加入路由,修改 frontend/src/config/router.config.js,把 asyncRouterMap 修改为如下内容

export const asyncRouterMap = [
  {
    path: "/",
    name: "index",
    component: BasicLayout,
    meta: { title: "menu.home" },
    redirect: "/helloworld",
    children: [
      // helloworld
      {
        path: "/helloworld",
        name: "hello-world",
        component: HelloWorld,
        meta: {
          title: "menu.hello",
          icon: "folder",
          permission: ["dashboard"],
        },
      },
    ],
  },
  {
    path: "*",
    redirect: "/404",
    hidden: true,
  },
];

此时,菜单的名字将显示 menu.hello,原因是我们没有在国际化中添加对 menu.hello 词条的翻译选项,我们修改 frontend/src/locales/lang/en-US.js 增加如下选项

const locale = {
  message: '-',
  'menu.home': 'Home',
  'menu.dashboard': 'Dashboard',
  'menu.hello': 'Hello', // 新增的内容

同样修改 frontend/src/locales/lang/zh-CN.js

const locale = {
  message: '-',
  'menu.home': '主页',
  'menu.hello': '问候', // 新增的内容

这样,我们就能看到正确的支持多语言的版本了。

添加对 API 请求

在 frondend/src/api/hello.js 中添加和 hello 相关的 api

import { axios } from "@/utils/request";

const api = {
  Hello: "hello",
};

export default api;

export function getHello() {
  return axios.get(api.Hello, {});
}

为了调试方便,我们使用 mock 功能在没有后端联调情况下模拟后端操作, 修改 frontend/src/views/helloworld/HelloWorld.vue

<template>
  <div>
    <h1>{{ loading }}</h1>
    <h1>{{ data }}</h1>
  </div>
</template>

<script>
import { getHello } from "@/api/hello";
import { PageView } from "@/layouts";

export default {
  name: "HelloWorld",
  components: {
    PageView,
  },
  data() {
    return {
      data: [],
      loading: false,
    };
  },
  mounted() {
    this.fetch();
  },
  watch: {
    $route(to, from) {
      this.fetch();
    },
  },
  computed: {
    title() {
      return `hello, world`;
    },
  },
  methods: {
    fetch() {
      this.loading = true;
      return getHello()
        .then((data) => {
          this.loading = false;
          this.data = data;
        })
        .catch((err) => {
          this.$notification.error({
            duration: null,
            message: err,
          });
          this.loading = false;
        });
    },
  },
};
</script>

这样,访问首页时就能够看到后台模拟 mock 接口传回的数据。

将 vue 编译,由 fastapi 托管

编译 vue 代码,生成的产品正式代码在 dist 目录下

yarn install && yarn run lint --no-fix && yarn run build --mode production

把编译好的 dist 代码全部移动到 backend 的 public 目录下。这个过程我们可以使用 bash 脚本自动化

#!/bin/bash
function fvtlog() {
    echo 【 FastAPIVueTemplate Build 】====\> $@
}

build_dir=$(pwd)
client_dir=$(pwd)/frontend
server_dir=$(pwd)/backend

ENV=$1
ENV="${ENV:-production}"

function buildClient() {
    fvtlog "start building client for FastAPIVueTemplate..."
    cd $client_dir
    yarn install && yarn run lint --no-fix && yarn run build --mode $ENV
}

function buildServer() {
    fvtlog "start build server for FastAPIVueTemplate"
    rm -rf $server_dir/public
    cd $build_dir
    mv $client_dir/dist $server_dir/public
}

buildClient
buildServer

此时,我们需要配置 fastapi 完成以下的路由

  1. 对/api 请求使用 fastapi 的处理逻辑
  2. 对/api 下的其他未实现请求返回 404
  3. 对所有其他请求尝试加载 public 目录下的资源文件,若失败,则加载 index.html,但路径仍保持请求路径。

这就是 vue 文件托管的 html5 history mode https://router.vuejs.org/guide/essentials/history-mode.html

修改 backend/app/init.py 为如下内容

import os.path
import time
from fastapi import  FastAPI
from fastapi.staticfiles import StaticFiles

# Use this to serve a public/index.html
from starlette.responses import FileResponse
from starlette.responses import PlainTextResponse

app = FastAPI()

@app.post("/api/auth/login")
def get_auth_login():
    data = {
        'id': 'id_001',
        'name': 'admin',
        'username': 'admin',
        'password': '',
        'avatar': 'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png',
        'status': 1,
        'telephone': '',
        'lastLoginIp': '27.154.74.117',
        'lastLoginTime': 1534837621348,
        'creatorId': 'admin',
        'createTime': 1497160610259,
        'deleted': 0,
        'roleId': 'admin',
        'lang': 'zh-CN',
        'token': '4291d7da9005377ec9aec4a71ea837f'
    }
    return dict(result=data, message='', code=200, _status=200)

@app.get("/api/user/info")
def get_user_info():
    info = {
        'id': '4291d7da9005377ec9aec4a71ea837f',
        'name': '天野远子',
        'username': 'admin',
        'password': '',
        'avatar': '/avatar2.jpg',
        'status': 1,
        'telephone': '',
        'lastLoginIp': '27.154.74.117',
        'lastLoginTime': 1534837621348,
        'creatorId': 'admin',
        'createTime': 1497160610259,
        'merchantCode': 'TLif2btpzg079h15bk',
        'deleted': 0,
        'roleId': 'admin',
        'role': {}
    }
    role = {
        'id': 'admin',
        'name': '管理员',
        'describe': '拥有所有权限',
        'status': 1,
        'creatorId': 'system',
        'createTime': 1497160610259,
        'deleted': 0,
        'permissions': [{
        'roleId': 'admin',
        'permissionId': 'dashboard',
        'permissionName': '仪表盘',
        'actions': '[{"action":"add","defaultCheck":false,"describe":"新增"},{"action":"query","defaultCheck":false,"describe":"查询"},{"action":"get","defaultCheck":false,"describe":"详情"},{"action":"update","defaultCheck":false,"describe":"修改"},{"action":"delete","defaultCheck":false,"describe":"删除"}]',
        'actionEntitySet': [{
            'action': 'add',
            'describe': '新增',
            'defaultCheck': False
        }, {
            'action': 'query',
            'describe': '查询',
            'defaultCheck': False
        }, {
            'action': 'get',
            'describe': '详情',
            'defaultCheck': False
        }, {
            'action': 'update',
            'describe': '修改',
            'defaultCheck': False
        }, {
            'action': 'delete',
            'describe': '删除',
            'defaultCheck': False
        }],
        'actionList': None,
        'dataAccess': None
    }]}
    info['role'] = role
    return dict(result=info, message='', code=200, _status=200)

@app.post("/api/auth/2step-code")
def auth_2step_code():
    return { 'stepCode': 0 }

@app.get("/api/hello")
def hello():
    return {'message': 'hello world when '+str(int(time.time()))}

@app.get("/api/{wildcard}")
async def get_api_404():
    return PlainTextResponse("api not found")

@app.get("/")
async def get_index():
    return FileResponse('public/index.html')

@app.get("/{whatever:path}")
async def get_static_files_or_404(whatever):
    # try open file for path
    file_path = os.path.join("public",whatever)
    if os.path.isfile(file_path):
        return FileResponse(file_path)
    return FileResponse('public/index.html')

有几个要点:

  1. app.get 的参数是路径名,注意和顺序相关,优先匹配最早的符合。另外,如果要匹配的参数中包括/,那么需要在参数后加:path 修饰符
  2. 按照顺序分别匹配/api 路径,根目录,然后将所有未匹配请求发送给最后一个函数。如果找到静态文件则返回其内容,否则就直接用 index.html 的内容返回。vue 会处理具体的路由渲染规则。
  3. ant design pro 本身自带权限控制系统,但是需要注意的是,原有的 mock 文件中的响应是需要加上包装的,比如 result 等于 mock 中 builder 的参数。这个没有文档提及,这块浪费了很多调试时间。
  4. 还有一些接口没有实现,这个文件只是包含了刚好能够让登陆用户进入系统的最小 api 需要的接口

总结

总体而言,fastapi+vue 的部署方式简单高效,能满足一般应用类网站特别是中台的开发需求。但在开发过程中需要注意将 vue 的编译文件托管给 fastapi 时的相关配置。

项目地址:https://github.com/elprup/fastapi-vue-template

相关引用

golang+vue 的实现例子 https://github.com/Sloaix/Gofi

fastapi-vue-template's People

Contributors

elprup avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.