1_初步了解和试运行
-
前言
本人原来使用的是Flask框架(公司在用),掌握的也不是很好,处于简单会用的层面。然后,公司想转用FastAPI,毕竟能火起来的技术自有可取之道,在此记录自己的学习过程。
学习过程中,主要观看了以下学习视频: -
目录
-
虚拟环境(非必须)
虚拟环境是啥这里就不加以解释,简单理解就是每个项目有个独立的运行环境,不会相互影响,毕竟有时你会遇到如使用 python2 和 python3的不同项目,共用一个运行环境可能无法同时适配。
-
前提
安装好python,并配置好环境变量。(可在命令窗口输入 python 指令查看) -
安装虚拟环境(virtualenv)
- 使用python自带的 pip 包管理工具安装virtualenv
- 进入命令窗口,输入 pip install virtualenv (若有多个python 版本,pip默认安装到python2, pip3安装到python3)
- 命令窗口进入项目目录或者文件夹右键Git Bash 打开命令窗口(前提你有装Git),执行 virtualenv envname (envname指虚拟环境名,一般设为 venv 即可,当然你可以设置成其他名字)
- 进入和退出虚拟环境(activate 和 deactivate)
- 前提
命令窗口进入项目所在目录,编辑器打开终端一般就是在项目的根目录了 - 进入(执行命令)
windows: ./venv(即envname)/Scripts/activate
Linux: source ./venv/bin/activate
虚拟环境进入成功,窗口路径前面会添加虚拟环境名,如:(venv)E:\admin_space\fast-api-example> - 退出(已进入虚拟环境)
windows: deactivate
Linux: deactivate
- 前提
-
-
安装FastAPI
若使用虚拟环境,需提前进入虚拟环境后执行命令
- pip install fastapi web框架
- pip install uvicorn web服务器(flask用的是werkZeug),其实定义通信网关接口的工具集的库
在Flask项目中,习惯用txt文件管理安装的第三方库和版本(requirements.txt)
在根目录新建一个 requirements 的txt文件,(名字可随意),然后里面输入第三方库和版本
如:fastapi==0.55.1 uvicorn==0.11.5
其它扩展写法此处不涉及
保存后 执行 pip install -r requirements.txt 安装 -
试运行
按照官方文档里例子 FastAPI
新建一个 main.py 文件,按本人以前Flask项目习惯,我用runanpp.py(代替),意指整个程序的入口,即程序启动文件。··· # -*- coding: utf-8 -*- from fastapi import FastAPI app = FastAPI() @app.get("/") async def main(): return {"message": "Hello World!"} # 启动服务1(类似java的main函数) if __name__ == '__main__': import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000) # 启动服务2,支持自动重启(类似java的main函数) if __name__ == '__main__': import uvicorn uvicorn.run(app="runapp:app", host="127.0.0.1", port=8000,reload=True) ···
若我们文件中添加了启动服务的脚本代码,则我们直接运行该文件,服务会自动启动:
即输入命令:python runapp.py (runapp.py 即 官方例子的 main.py)
一般项目有个一个程序入口,我们在这里做一下初始配置。
脚本扩展:
通过上面的脚本启动服务,修改代码后不会自动重启,查看函数,跟命令行一样,支持各种配置(如:添加 reload=True 支持重新启动)
注意:若要启用“重新加载”,即reload,应用程序必须作为字符串传入,即首个参数不能直接传入app,而是要参考命令行的写法,如 “app=runapp:app”,才能成功启动,不然会报错同样我们可以在命令行直接输入指令:uvicorn runapp:app --reload 启动程序
注意:
runapp,即文件名,不带文件后缀,看过一个讲解视频是带后缀,但我带后缀就报错,不能正常运行)
app,即程序实例对象,即你FastAPI赋值的变量名
–reload,即修改保存后,会自动重启这些都跟Flask很类似,注意文件所在的文件路径。
-
模版引擎
FatsAPI没有指定使用模版引擎,比较灵活,以前Flask用的Jinja2,后续也采用Jinja2来学习和实现网页
安装Jinja2,又使用静态文件,把aiofiles也装上
pin install jinja2
pin install aiofiles
通让的把这些库写到 requirements.txt文件中在项目中创建 templates 文件夹,把要创建的 html 模版文件都在放里面。
Jinja2的语法这里涉及,这里主要举例子。··· from fastapi import FastAPI from starlette.requests import Request from starlette.templating import Jinja2Templates app = FastAPI() # 获取模版路径 templates = Jinja2Templates(directory="./app/templates") @app.get("/") async def main(): return {"message": "Hello World!"} @app.get("/homes") async def main(request: Request): return templates.TemplateResponse( 'index.html', {'request': request, 'hello': 'hello Jinja2 12354'}) ···
通过starlette引入 Request 和 Jinja2Templates
函数中返回 templates.TemplateResponse(‘指定的模版文件’,Context),templates.TemplateResponse意思指返回 模版箱响应。
注意:与Flask相比,函数必须返回 Request对象,并作为参数传到模版中,不然会访问失败,而Flask默认是this
经过测试:
Context必须是包含 request 键,并且对应值支持 get 取值的对象,不然会报:
ValueError: context must include a “request” key 错误
in TemplateResponse
raise ValueError(‘context must include a “request” key’)前面提到函数返回request参数,并作为requst键的值是非必须的,作用等同于我们在Flask中使用模版时用的 this。
-
试用表单提交数据
表单提交数据,这里特指尝试POST提交方式。
新建一个post数据的页面,简单定义为create.html,里面代码大致如下:··· <div> <form action="http://127.0.0.1:8000/users" method="post" enctype="application/x-www-form-urlencoded"> <label>用户名:</label> <br> <input type="username" name="username"> <br> <label>密码:</label> <br> <input type="password" name="password"> <br> <input type="submit" value="提交"> <br> </form> </div> ···
action属性指定数据提交路径,可以用相对路径,这里我用了绝对路径。
input中name属性的值,等同请求时的参数名。新建一个数据展示页面,简单定义为user.html,里面代码大致如下:
··· <div> <div> <label>用户名:</label> <br> <span>{{ username }}</span> <br> </div> <div> <label>密码:</label> <br> <span>{{ password }}</span> <br> </div> </div> ···
主要用于展示create.html表单中提交的内容。
这里我们需要分别访问两个页面:
··· # 用到Form(...),需要引入Form,同样在fastapi中 from fastapi import FastAPI, Form # 访问表单页面 @app.get("/create") async def create_user(request: Request): data = {'request': request} return templates.TemplateResponse('create.html', data) # 获取表单数据并展示(这里用post,与表单的method属性设置一致) @app.post("/users") async def user_info(request: Request, username: str = Form(...), password: str = Form(...)) data = { 'request': request, 'username': username, 'password': password} return templates.TemplateResponse('user.html', data) ···
注意:使用Form(…),还需要安装第三方库 python-multipart
pip install python-multipart (方便管理,我们也写进requirements.txt中)Form(…) 的作用是:获取表单中对应的数据,函数中使用该方式获取参数,必须是post请求方式,get请求会报错。(其默认接收表单类型是:“application/x-www-form-urlencoded”)
我们在Flask中获取表单数据(WerkZeug):request.form.get(‘参数名’, ‘’),而Form则是帮我们在传参时完成这些操作,提高开发效率,优化函数内部代码,专注于业务,而非获取参数。
(注意:参数名需与表单input标签中的name属性的值一致)在Flask中,我们都是通过操作request对象来进行参数操作,FastAPI同样支持,使用的是starlette的Request和Response,这里不过多的阐述,因为我也没用过starlette。
简单:- get请求:
request.query_params[‘参数名’] 或 request.query_params.get(‘参数名’, ‘’) (建议用get方式获取,避免参数不存在报错) - post请求:
form = await request.form() (form()是一个异步操作,需要加await等待结果)
form[‘参数名’] 或 form.get(‘参数名’, ‘’)
好像稍微麻烦一点。。。。而且不能享受数据强制验证带来的便利(Pydantic)
- get请求:
-
试用表单上传文件
-
导入新模块
··· # 接收列表类型需要 from typing import List # 文件操作相关 from fastapi import File, UplodFile ···
-
创建表单文件
··· <form action="/files" enctype="multipart/form-data" method="POST"> <input type="file" name="files_list" multiple><br><br> <input type="file" name="files_name" multiple><br><br> <input type="submit" value="上传"> </form> ···
-
进入上传页面
··· @app.get("/users") async def user_info(request: Request): data = {'request': request} return templates.TemplateResponse('user.html', data) ···
-
获取数据并展示
··· @app.post("/files") async def upload_files(request: Request, files_list: List[bytes] = File(...), files_name: List[UploadFile] = File(...)): data = { 'request': request, 'file_sizes': [len(file) for file in files_list], 'file_names': [file.filename for file in files_name] } return templates.TemplateResponse('user.html', data) ···
我提交和展示都写在user.html页面了,浏览器跳转指定页面要用get请求。
相较于提交普通的表单,上传文件用的是 File(…) 获取,作用类似Form(…),不深入阐释,因为也不是很懂。从typingm模块导入List的作用是为了接收列表类型的参数数据:
- files_list: List[bytes] = File(…) (允许接收多个文件)
- files_list: bytes = File(…) (只允许接收单个文件)
其它一些文件操作,如获取文件名、类型等操作(文件类型必须是UploadFile,是starlette的):
- files_name: List[UploadFile] = File(…)
FatsAPI支持bytes和UploadFile两种文件格式,而UploadFile相较bytes的优点有:
- 使用假脱机文件(超过内存文件存储限制,将存储在磁盘中)
- 适用于大型文件,不会占用所有内存
- 可以从上传的文件获取元数据
- 具有类似文件的接口
-
-
补充
- Form(…) 和 File(…) 中‘…’ 代表什么
理解为参数是必须的,为空会报错,Form(None)…则非必须
- Form(…) 和 File(…) 中‘…’ 代表什么