FastAPI与Vue的天作之合
在Web开发的世界里,有这么一对年轻男女,分别出身于FastAPI家族(法家)与Vue家族(魏家),他们家世显赫、门当户对。时代背景且听我一一道来。
在远古时期,前端与后端混杂在一起。那是一个门伐割据的时代,jsp、asp、php三大门伐牢牢的统治着这个世界。但是,各门伐内部管理却较为混乱,就jsp来说,html、css、javascript、java代码混淆在一个jsp文件中,虽分工明确,但却管理混乱。要想他们配合做一些事情,着实不简单。
后来,javascript因对地位产生了不满,发动了一场Ajax革命。html、css追随javascript一起,渐渐脱离了java等贵族的掌控。Web开发逐渐出现了两股势力:前端和后端。随着jQuery家族的大行其道,前后端的分离越来越明显,但是,前端始终依赖于后端模板技术,无法完全独立。
Node.js的诞生拉开了近代史的序幕,前端终于可以独立的运行。但是新的问题逐渐显露,jQuery家族管理下的html、css、javascript依然是混乱的状态。javascript的强势还是引起了html和css的不满,一场革命又在酝酿这中。不久,AngularJS家族诞生了,它不再允许javascript操作DOM,这一点html和css非常赞同,于是,jQuery家族慢慢势弱。于此同时,新兴的AngularJS家族在发展过程中也暴露出了不少问题,没有办法进一步的解放生产力。于是又诞生了一个强大的家族:React。直到今日,React家族一直占据着前端开发的统治地位。
React家族的强大,是依赖于它事无巨细的法律,而这法律庞大到让人很难掌握。慢慢的,我们的女主角所在的Vue家族开始兴起。Vue家族推行精兵建政的发展策略,用的法律简单有效,得到了皇家的世间的认可。今日,Vue家族虽不能和React家族分庭抗礼,但实力却也不可小觑。
前端势力的蓬勃发展,意味着后端势力的逐渐衰退。在这样的时代背景下,后端势力,从Django到Tornado、Flask,再到男主所在的FastAPI家族。一方面,模板文件中,后端的代码越来越少,甚至FastAPI家族,不再拥有模板业务;另一方面,在抛开了前端的包袱后,后端势力向着高精尖的方向大步迈进,开发越来越精简、开发效率越来越高,从WSGI到ASGI,后端开发模式有了质的飞跃。
我们的男女主角所在的两大家族,有着一个共同的特点:简单高效。因此,他们两家可当真称的上是门当户对,两家的儿女,必定可以成就美好的姻缘。
魏家有女初长成
Vue家族自2013年才刚刚成立,不可谓不新,成长初期就能够在前端势力中占有举足轻重的地位,靠的是他的独门绝技: 虚拟DOM 。dom操作是一个非常消耗性能的操作,Vue凭借虚拟DOM技术,不再使用原生的dom操作节点,极大的解放了dom操作。 而虚拟DOM又能将视图和数据很好的分隔开来,同时在两者之间建立双向绑定的快车道,可谓一箭双雕,一石二鸟。
要了解更多Vue家族的知识,可到Vue官网学习。
明宵新月初生晕
首先搭建Vue3开发环境:
-
安装Node.js:
从Node.js官网下载长期支持版,并进行安装。
-
为npm配置淘宝源:
Shellnpm config set registry https://registry.npm.taobao.org
-
安装vue3:
Shellnpm install vue@next
-
安装create-vite
Shellnpm i create-vite -g
接下来就可以创建Vue工程了:
-
创建vite工程:
Shellnpm init vite
-
设置工程名,我们的项目名为bride(新娘):
ShellProject name: ... bride
-
选择工程类型:
-
选择变体:
-
安装工程依赖:
Shellcd bride npm i
这样,Vue家族的小美女就诞生了。
云想衣裳花想容
前端界面要做的漂亮,离不开UI库的支持,基于Vue的UI库有很多,其中较为出名的,要数Element。Element是由饿了么公司出品的开源的UI库,它有两个版本:
- Element UI:基于Vue2
- Element Plus:基于Vue3
我们选择了Vue3,就需要安装Element Plus:
Shell
npm i element-plus # 安装Elements Plust
npm install @element-plus/icons-vue # 安装图标库
安装完成后,在main.js引入Element Plus:
JavaScript
// 创建vue应用
import { createApp } from 'vue'
import App from './App.vue'
let app = createApp(App);
// 引入并使用Element Plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus);
// 引入Element Plus的图标库
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
// 挂载dom
app.mount('#app')
然后就可以在vue页面中随心所欲的用Element来为小美女梳妆打扮了。
要了解Element各组件的使用,请移步Element Plus官网。
八面玲珑七窍心
前后端完全分离之后,路由不再是只有后端才拥有的技术。我们的小美女现在只能完成单页面应用程序,需要借助vue-router的路由功能,让我们的小美女拥有多页面的能力。
与Element一样,使用npm安装vue-router,但也需要注意要安装Vue3的版本:
Shell
npm i vue-router@next
安装完成后,在main.js中创建路由:
JavaScript
import {createRouter, createWebHistory} from "vue-router" // 从vue-router中引入createRouter和createWebHashHistory
import hello from "./components/hello.vue" // 引入hello页面
import interest from "./components/interest.vue" // 引入interest页面
import file from "./components/file.vue" // 引入file页面
// 创建路由
const VueRouter = createRouter({
// 使用createWebHistory来管理浏览历史,如使用createWebHashHistory,URL后面会被加上#,很不舒服
history: createWebHistory(),
// 路由映射
routes:[
{
path: "/",
redirect: "/hello" // 跳转到/hello
},
{
path: '/hello',
component: hello // 渲染hello页面
},
{
path: '/interest',
component: interest // 渲染interest页面
},
{
path: '/file',
component: file // 渲染file页面
}
]
})
app.use(VueRouter)
在模板中,可以使用router-link组件在页面间进行跳转:
html
<router-link to="/hello">点我跳转</router-link>
在App.vue中,最上方使用Element Plus提供的导航条,可以很好的与vue-router配合。导航条下方,使用router-view组件,嵌入路由子页面:
html
<script>
</script>
<template>
<!-- 导航栏:指定router属性,开启vue-router -->
<el-menu class="el-menu-demo" mode="horizontal" :router="true">
<el-menu-item index="/hello">互通姓名</el-menu-item>
<el-menu-item index="/interest">交流兴趣</el-menu-item>
<el-menu-item index="/file">互递情书</el-menu-item>
</el-menu>
<!-- 使用router-view组件,嵌入路由子页面 -->
<router-view></router-view>
</template>
<style scoped>
</style>
不要多想,只要写上router-view组件,并对它进行布局就可以了。至于router-view组件里面渲染的是哪个页面,vue-router会帮我们处理。
是不是很简单,我们的小美女已经长大成人了。
当然,路由并非那么简单的事情,还有很多高级应用,可到vue-router官网进行了解。
法家才子学艺精
FastAPI 是一个用于构建 API 的现代、快速(高性能)的web框架,使用Python 3.6+并基于标准的Python类型提示。
它拥有如下关键特性:
- 快速:可与 NodeJS 和 Go 比肩的极高性能(归功于 Starlette 和 Pydantic)。是最快的Python web框架之一。
- 高效编码:提高功能开发速度约 200% 至 300%。*
- 更少 bug:减少约 40% 的人为(开发者)导致错误。*
- 智能:极佳的编辑器支持。处处皆可自动补全,减少调试时间。
- 简单:设计的易于使用和学习,阅读文档的时间更短。
- 简短:使代码重复最小化。通过不同的参数声明实现丰富功能。bug 更少。
- 健壮:生产可用级别的代码。还有自动生成的交互式文档。
- 标准化:基于(并完全兼容)API 的相关开放标准:OpenAPI (以前被称为 Swagger) 和 JSON Schema。
还有一点不得不夸赞FastAPI,他官网做的很棒,尤其是中文翻译,虽然现在还没有翻译全,但绝对不是机器翻译,文档可读性很好,甚至一些成语用得都很到位:FastAPI官网:
文曲下凡降人间
安装FastAPI,可以直接使用pip进行安装,它有两个必要的库:
Shell
pip install fastapi
pip install uvicorn
FastAPI还依赖几个非必要的库,可以使用下面的命令一次性全部安装:
Shell
pip install fastapi[all]
创建FastAPI项目,没有什么手脚架可以使用,因为它简单到写几行Python代码即可完成,如groom.py:
Python
from fastapi import FastAPI
import uvicorn
# 创建app
app = FastAPI(
title="FastAPI groom",
description="FastAPI家族的新郎官"
)
# 创建根路由
@app.get("/")
def hello():
"""
打个招呼
"""
return "hello world"
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run("groom:app", host="0.0.0.0", port=8080)
名师指导学有成
浏览FastAPI官网介绍的各种特性时,你会发现官方有很多类似于"XXXX特性基于XXXX"的描述。是的,FastAPI聘请了天下名师指导家族子弟。
- 自动生成文档
像Django那样,有一个自动 API 文档,开发者可以通过Web界面进行调用自己的接口 ,后端开发就可以不再依赖前端进行测试。FastAPI也提供了类似的自动 API 文档,还一下提供了两个。一个基于Swagger UI,另一个基于ReDoc。
- 微框架
像Flask那样,只提供一个微框架,而不是像Django那样提供大而全的巨型框架。 FastAPI拥有一个简单易用的路由系统 ,可以混合其他库提供的工具。这是许多现代工具的思想,自身的包袱小了,才可以海纳百川有容乃大。借用官网的话,叫做" 导入并使用你需要的代码,而不需要它们 "。集成而非依赖,是微框架的哲学。
- 简单直观的API
有史以来下载次数最多的Python库是什么? Requests 是其中之一。 Requests是API的客户端,而FastAPI是API的服务器。 Requests 具有非常简单直观的设计,各接口具有合理的默认值,非常易于使用。FastAPI家族的业务与之相对,却从Requests那学习到了它的精髓。
- 自动化的数据验证
接口开发最担心什么?不是担心业务逻辑多么复杂,而是担心客户端不按约定的方式调用。使用Tornado开发API时,最先要做的事情就是对各种各样的参数进行验证和类型转换。 Webargs 、Flask都提供了自动化的数据验证,但都不如FastAPI来的直接。因为FastAPI采用了Python3.6以来类型声明系统,不但可以做到自动化的数据验证,还可以将数据的类型生成到接口文档中。
- 异步
Sanic是最早基于 asyncio的极端快速Python框架之一。那一代Web框架完成了从WSGI到ASGI的革命。FastAPI选择Starlette, Starlette是一种轻量级的ASGI框架/工具包,是目前测试最快的Python框架,是构建高性能asyncio服务的理想选择。 FastAPI本身直接继承Starlette。因此,使用Starlette可以执行的任何操作,都可以直接使用FastAPI进行 。 Uvicorn是基于uvloop和httptools构建的如闪电般快速的ASGI服务器。FastAPI选择它作为推荐服务器,可以进一步提供高性能的解决方案。
FastAPI可谓站在巨人的肩膀上,集各家所长于一身。FastAPI家族的子弟,岂有不优秀的道理。
接口文档作媒人
要说我最喜欢的FastAPI的功能,就是自动生成接口文档。它的文档结合自动化的数据验证的类型系统,全面而又通俗易懂。不但有文档,还可以在文档中测试。把这样的接口文档作为媒界。后端开发者可以用来自己测试,前端开发者也可以验证自己的理解是否正确。
例如我有一个接口:
Python
from fastapi import FastAPI
from pydantic import BaseModel # 引入BaseModel
import uvicorn
# 创建app
app = FastAPI(
title="测试文档",
description="这是一个简单的 demo"
)
class Person(BaseModel): # 创建Person类,继承自BaseModel。这是一个数据类
name: str # 定义姓名字段
gender: str # 定义性别字段
# 创建根路由
@app.post("/")
def hello(p:Person): # 指定p的类型为Person,注意不要写成p=Person
"""
打个招呼
"""
return f"hello {p.gender} {p.name}"
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run("hello:app", host="0.0.0.0", port=80)
运行之后,在浏览器中访问/docs,便可以看到Swagger UI风格的接口文档:
点击Try it out可以直接在页面上测试:
如果你不喜欢,那就访问/redoc,来看ReDoc风格的文档:
FastAPI聘请了这样两位媒人,何愁不能抱得美人归。
金风玉露一相逢
Ajax互传情
FastAPI提供了一系统接口,Vue就可以通过Ajax进行调用,在调用之前,先要安装axios:
Shell
npm i axios
然后,在main.js中引用axios:
JavaScript
// 全局引用axios,不能像element plus那像使用use
import axios from 'axios'
app.config.globalProperties.$axios = axios
这样引用之后,可以在Vue页面中使用:
- 组合式API中:proxy.$axios
- 选项式API中:this.$axios
男女授受不亲的思想,在Web开发的世界也有着不小的影响力,Vue家族规定自家小姐不能直接与外人见面,如果想要互相通信,需要由Vue家族的代理人转达。
代理人的配置,在vite.config.js中:
JavaScript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
port: 80, // 默认端口
proxy: { // 代理
'/api': { // 接口前缀
target: 'http://127.0.0.1:8080/', // 接口域名
changeOrigin: true, // 允许跨域
ws: true, // 是否代理websocket
}
}
}
})
这段代码配置了一个代理人/api,Vue家的小姐姐可以通过它(以/api为前缀)来访问http://127.0.0.1:8080/
的接口。
准备工作已经就绪,二人的第一次约会就被提上的日程。
FastAPI家的才子写了下面的接口:
Python
from fastapi import FastAPI
import uvicorn
# 创建app
app = FastAPI(
title="FastAPI groom",
description="FastAPI家族的新郎官"
)
# 创建打招呼路由
@app.get("/api/hello")
def hello(name):
"""
打个招呼
"""
return f"你好 {name}。 我是FastAPI家族的小男生。名叫法斯特。"
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True # 开启自动加载功能
)
于是媒人来到Vue家,邀请Vue家小姐姐茶楼相亲:
Vue家的小姐姐看到接口文档后,欣然赴约:
JavaScript
<template>
<el-row>
<el-col :span="4">
姓名:
</el-col>
<el-col :span="4">
<el-input v-model="name"></el-input>
</el-col>
<el-col :span="2">
<el-button type="primary" @click="hello">打招呼</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="4">
对方答复:
</el-col>
<el-col :span="20">
{{ res }}
</el-col>
</el-row>
</template>
<script>
export default {
name: "hello",
data() {
return {
name: "魏有依", // 小美女姓名
res: "" // 对方答复
}
},
methods: {
// 打招呼
hello() {
this.$axios.get(
"/api/hello",
{
params: {
name: this.name
}
}
)
.then((response) => {
console.log(response)
this.res = response.data;
})
.catch((error) => {
console.log(error);
});
}
}
}
</script>
<style scoped>
</style>
约会成果如图:
就这样,二人互通了姓名,建立了恋爱关系。直白的GET,变成了较为私密的POST。
FastAPI家的才子为了更多的了解对方,又写了下面的接口:
Python
from fastapi import FastAPI
from pydantic import BaseModel # 引入BaseModel
import uvicorn
# 创建app
app = FastAPI(
title="FastAPI groom",
description="FastAPI家族的新郎官"
)
lass Interest(BaseModel): # 兴趣类
color: str # 喜欢的颜色
food: str # 喜欢的食物
fruit: str # 喜欢的水果
movement: str # 喜欢的运动
# 创建聊兴趣路由
@app.post("/api/interest")
def interest(interest: Interest):
"""
了解兴趣爱好
"""
return {
"color": interest.color,
"food": interest.food,
"fruit": interest.fruit,
"movement": interest.movement
}
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True # 开启自动加载功能
)
辛苦媒人又跑了一趟:
Vue家的小姐姐看到接口文档后,将自己的兴趣爱好告诉对方,同时得到了对方的信息:
Plain
<template>
<el-row>
<el-col :span="10">
<el-row>
<el-col :span="24"><h1>女方兴趣</h1></el-col>
</el-row>
<el-row>
<el-col :span="4">颜色:</el-col>
<el-col :span="20"><el-input v-model="bride.color"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">食物:</el-col>
<el-col :span="20"><el-input v-model="bride.food"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">水果:</el-col>
<el-col :span="20"><el-input v-model="bride.fruit"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">运动:</el-col>
<el-col :span="20"><el-input v-model="bride.movement"></el-input></el-col>
</el-row>
</el-col>
<el-col :span="4">
<el-button type="primary" @click="interest" class="interest">聊兴趣</el-button>
</el-col>
<el-col :span="10">
<el-row>
<el-col :span="24"><h1>男方兴趣</h1></el-col>
</el-row>
<el-row>
<el-col :span="4">颜色:</el-col>
<el-col :span="20"><el-input v-model="groom.color"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">食物:</el-col>
<el-col :span="20"><el-input v-model="groom.food"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">水果:</el-col>
<el-col :span="20"><el-input v-model="groom.fruit"></el-input></el-col>
</el-row>
<el-row>
<el-col :span="4">运动:</el-col>
<el-col :span="20"><el-input v-model="groom.movement"></el-input></el-col>
</el-row>
</el-col>
</el-row>
</template>
<script>
export default {
name: "interest",
data() {
return {
// 女方兴趣
bride: {
color: "红色",
food: "馒头",
fruit: "苹果",
movement: "逛街"
},
// 男方兴趣
groom: {
color: "",
food: "",
fruit: "",
movement: ""
},
}
},
methods: {
interest() {
this.$axios.post(
"/api/interest",
this.bride
)
.then((response) => {
this.groom = response.data;
})
.catch((error) => {
console.log(error);
}
);
}
}
}
</script>
<style scoped>
.interest {
position: relative;
top: calc(100% / 2);
left: calc(100% / 2 - 42px);
}
</style>
约会成果如图:
这次约会意义非凡,二人发现他们的兴趣爱好竟然一模一样,感觉彼此找到了知己。于是二人感情急剧升温,不再局限于说话聊天,开始了互通情书。
第一封情书是FastAPI家的才子写的,这是Starlette老师教给他的技能。写好之后,坐立不安,等等着Vue家的小姐姐前来下载:
Python
from fastapi import FastAPI
from starlette.responses import FileResponse # 引入FileResponse
import uvicorn
# 创建下载情书路由
@app.get("/api/download")
def download():
"""
下载情书
"""
return FileResponse(
r"D:\test\Python\fastapi\marry\groom\情书.docx", # 实际文件路径
filename="魏有依小姐亲启.docx" # 浏览器上看到的文件名
)
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True # 开启自动加载功能
)
媒人:
终于,Vue家的小姐姐的下载请求来了:
JavaScript
this.$axios.request({
url: "/api/download",
method: "get",
responseType: "blob"
})
.then(response=>{
console.log(response.headers);
let fileinfo = response.headers["content-disposition"];
console.log(fileinfo);
const blob = new Blob([response.data]);
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob);
downloadElement.href = href;
downloadElement.download = decodeURIComponent(fileinfo.split("filename*=utf-8''")[1]);
document.body.appendChild(downloadElement);
downloadElement.click();
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href);
})
.catch((error) => {
console.log(error);
});
情书取走之后,FastAPI家的才子激动万分,连夜写了回信上传接口:
Python
from fastapi import FastAPI, File, UploadFile # 引入File和UploadFile
import uvicorn
# 创建app
app = FastAPI(
title="FastAPI groom",
description="FastAPI家族的新郎官"
)
# 创建上传情书路由。因文件上传时间较长,此接口为异步接口
@app.post("/api/upload")
async def upload(file: UploadFile = File(...)):
# 使用协程读取上传的文件
contents = await file.read()
# 保存文件内容
with open(file.filename, "wb") as fp:
fp.write(contents)
return {"filename": file.filename, "content_type": file.content_type}
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True # 开启自动加载功能
)
媒人,走你:
Vue家的小姐姐看完情书后,心情澎湃,没等多久,就写好了回信,并完善了情书传递的代码:
Plain
<template>
<el-row>
<el-col>
<el-button type="primary" @click="download">下载情书</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="10">
<el-upload
drag
class="upload-demo"
action="/api/upload"
:file-list="files"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
</el-col>
</el-row>
</template>
<script>
export default {
name: "file",
data() {
return {
files: []
}
},
methods: {
download() {
this.$axios.request({
url: "/api/download",
method: "get",
responseType: "blob"
})
.then(response=>{
console.log(response.headers);
let fileinfo = response.headers["content-disposition"];
console.log(fileinfo);
const blob = new Blob([response.data]);
let downloadElement = document.createElement("a");
let href = window.URL.createObjectURL(blob);
downloadElement.href = href;
downloadElement.download = decodeURIComponent(fileinfo.split("filename*=utf-8''")[1]);
document.body.appendChild(downloadElement);
downloadElement.click();
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href);
})
.catch((error) => {
console.log(error);
});
}
}
}
</script>
<style scoped>
</style>
结果如下:
Cookie相通定终身
Cookie在Web开发中的地位举足轻重,前后端之间可通过Cookie传递很多信息。
事实上,他们两人在第一次约会的时候,就已经偷偷的通过Cookie互换了生辰八字。只是因为Cookie传输比较隐蔽,没有人知道而已。
FastAPI家的才子真正的hello接口是这样的:
Python
from fastapi import FastAPI, Response, Cookie # 引入Response和Cookie
import uvicorn
# 创建app
app = FastAPI(
title="FastAPI groom",
description="FastAPI家族的新郎官"
)
# 创建打招呼路由
@app.get("/api/hello")
def hello(name, res: Response, b_horoscope=Cookie(None)): # res参数用来写入Cookie, b_horoscope参数直接由Cookie中读取出来
"""
打个招呼
"""
print(b_horoscope) # 打印女方的生辰八字
res.set_cookie(key="g_horoscope", value="2022-07-28") # 写入男方生辰八字
return f"你好 {name}。 我是FastAPI家族的小男生。名叫法斯特。"
# 运行uvicorn服务器
if __name__ == "__main__":
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True # 开启自动加载功能
)
Vue家稍微有一点点麻烦,要想使用Cookie,需要安装vue-cookies:
Shell
npm i vue-cookies
然后在main.js中引入,并且打开axios的Cookie开关:
JavaScript
// 引入vue-cookies
import VueCookies from 'vue-cookies';
app.config.globalProperties.$cookies = VueCookies;
// 全局引用axios,不能像element plus那像使用use
import axios from 'axios'
axios.defaults.withCredentials=true //开启cookie
app.config.globalProperties.$axios = axios;
然后调用hello接口的代码如下:
JavaScript
// 打招呼
hello() {
// 设置Cookie
this.$cookies.set("b_horoscope", "2022-07-28");
this.$axios.get(
"/api/hello",
{
params: {
name: this.name
}
}
)
.then((response) => {
console.log(response)
this.res = response.data;
const g_horoscope = this.$cookies.get("g_horoscope");
console.log(g_horoscope);
})
.catch((error) => {
console.log(error);
});
}
原来,他们早就瞒着所有人私定终身了啊!
天作之合结同心
二人的婚事已经定下。下面就是两家人分别为大婚做准备了。
一身红妆待出嫁
Vue家准备婚事比较简单,带着嫁妆,唱着歌,就可以过门了。
简单归简单,也是要细心准备才是。嫁妆其实还是很多的,主要集中的工程的/scr/assets中。要用的js工具啦、化妆的css和图片啦,少了一个也不行。
Vue家有位管家,叫做Vite,是一位做事很周到的人。一般用的嫁妆他都能给整理的井井有条。但是有一些小姐自己想带的(js中引用的图片),还是得知会管家一声,不然的话出现遗漏就不好了。
小姐的丫鬟(globEager)负责整理这个清单,把这个清单写到页面的数据中:
JavaScript
export default {
data() {
return {
extras: import.meta.globEager("/scr/assets/icon/*.svg"), // 额外的图片清单
img_path: "" // 要显示的图片路径
}
}
}
然后小姐要用的话,只须要在模板里说一声:
HTML
<img :src="extras[path].default"/>
再就是index.html中,引用了public中的vite.svg,我们也把它放到嫁妆里面,然后在修改在下index.html的路径:
HTML
<link rel="icon" type="image/svg+xml" href="/src/assets/vite.svg" />
到此,准备工作就已经做好了,可以通知管家打包了:
Shell
npm run build
嫁妆准备了一马车,停在项目的/dist中,出嫁的时候把马车赶过去就好了。
鸳鸯终结两同心
FastAPI家的准备工作就要多一些了,最主要的就是宅子的安排上。新郎官亲自出马,将/api开头的接口整理到一个文件中,并让APIRouter做它们的小组长。搬完家的apis.py是这样的:
Python
from fastapi import APIRouter, File, UploadFile, Response, Cookie
from pydantic import BaseModel
from starlette.responses import FileResponse
# 实例化路由器
router = APIRouter(prefix="/api")
# 创建打招呼路由
@router.get("/hello")
def hello(name, res: Response, b_horoscope=Cookie(None)):
"""
打个招呼
"""
print(b_horoscope)
res.set_cookie(key="g_horoscope", value="2022-07-28") # 写入男方生辰八字
return f"你好 {name}。 我是FastAPI家族的小男生。名叫法斯特。"
class Interest(BaseModel): # 兴趣类
color: str # 喜欢的颜色
food: str # 喜欢的食物
fruit: str # 喜欢的水果
movement: str # 喜欢的运动
# 创建聊兴趣路由
@router.post("/interest")
def interest(interest: Interest):
"""
了解兴趣爱好
"""
return {
"color": interest.color,
"food": interest.food,
"fruit": interest.fruit,
"movement": interest.movement
}
# 创建下载情书路由
@router.get("/download")
def download():
"""
下载情书
"""
return FileResponse(
"情书.docx", # 实际文件路径
filename="魏有依小姐亲启.docx" # 浏览器上看到的文件名
)
# 创建上传情书路由。因文件上传时间较长,此接口为异步接口
@router.post("/upload")
async def upload(file: UploadFile = File(...)):
# 使用协程读取上传的文件
contents = await file.read()
# 保存文件内容
with open(file.filename, "wb") as fp:
fp.write(contents)
return {"filename": file.filename, "content_type": file.content_type}
再盖一座别院,名为views.py。这是专门给夫人住的:
Python
from fastapi import APIRouter
from fastapi.responses import HTMLResponse
# 实例化路由器
router = APIRouter()
# 读取index.html的内容
def get_index():
"""
读取index.html
"""
with open("index.html", "r") as fp:
return fp.read()
index = get_index()
@router.get("/", response_class=HTMLResponse) # 创建根路由
@router.get("/hello", response_class=HTMLResponse) # 创建打招呼路由
@router.get("/interest", response_class=HTMLResponse) # 创建聊兴趣路由
@router.get("/file", response_class=HTMLResponse) # 创建传情书路由
def hello():
return index
终于可以迎接新娘子了。新娘的马车到了以后,让新娘(index.html)和新郎(groom.py)住在一起,然后把嫁妆安置在assets目录中。欢天喜地的一场宴席之后,一对璧人终于如愿以偿的结为夫妻。
夫妻双双把家还
二人成亲后,称呼也变成了老爷和夫人。夫人负责貌美如花,老爷负责赚钱养家。家族的生意做的很大,老爷凡事都要亲力亲为,恨不得要把自己当成两个人用。于是,老爷请教既是老师,又是管家的uncorn。uncorn实为一为隐世高人,在他的帮助下,老爷竟然真的练成了一套分身之术:
分身术的使用也非常简单,只需要修改一下启动方式:
Python
uvicorn.run(
"groom:app", # 主文件名:App对像名
host="0.0.0.0", # 绑定IP
port=8080, # 绑定端口
reload=True, # 开启自动加载功能
workers=4 # 开启多进程服务
)
也可以直接抛弃main,使用uncorn来启动:
Shell
uncorn groom:app --host 0.0.0.0 --port 8080 --workers 4
有了分身术的加持,老爷办什么事都能举重若轻。FastAPI家族的生意顺风顺水,越做越大,直到有一天。老爷分再多的身也难以应对繁杂的事务了。
夫人建议直接出面帮老爷,不再通过老爷中转,老爷也终于意识到,凡事都要亲力亲为,实在是太过劳累。于是二人聘请了一位职业代理人:nginx。家族的生意发展成了下面的样子:
nginx不愧是专业的代理人,有些简单的问题,老爷只需要教一遍他就能学会(生成缓存),再有同样的问题不再需要经过老爷,nginx自己就可以处理。于是,老爷把家族业务分为两大部分,简单的事务(可缓存接口)教会了nginx就行了,核心业务(不可缓存接口)还是要自己牢牢掌握,不能交给外人打理。
nginx的配置如下:
conf
worker_processes auto;
error_log logs/error.log notice;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# 反向代理
upstream groom {
server 127.0.0.1:8080;
}
proxy_cache_path cache keys_zone=CACHE:1m levels=1:2 inactive=1m max_size=1g;
server {
listen 80;
server_name localhost;
location / {
root ../bride/dist; # 目录中包含index.html以及assets子目录
index index.html;
try_files $uri $uri/ /index.html;
}
# 可缓存业务
location ^~ /api/dsc/ {
proxy_pass http://groom$request_uri; # 转发到uncorn
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 缓存
proxy_cache CACHE;
proxy_cache_key "$request_uri|$request_body";
proxy_cache_min_uses 1; # 一个用户访问后就生成缓存
proxy_cache_methods POST; # 即使是post请求也进行缓存
proxy_cache_valid any 1m; # 缓存1分钟后失效
}
# 不可缓存业务
location ^~ /api/dsnc/ {
proxy_pass http://groom$request_uri; # 转发到uncorn
#以下三行,目的是将代理服务器收到的用户的信息传到真实服务器上
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}