快速编码,功能完善。从启动到部署,实例详解异步 py3 框架选择 FastAPI 的原因。

FastAPI 介绍

FastAPI 与其它 Python-Web 框架的区别

在 FastAPI 之前,Python 的 Web 框架使用的是 django、flask、tornado 三种 Web 框架。

  • django 自带 admin,可快速构建,但是比较笨重。如果是 mvc 形式的开发,很多已经封装好了,的确蛮合适。但如果是 restful 风格设计,则 django 就显得有一些笨重了。

  • flask 快速构建,自由度高。因为它十分轻盈,插件即插即用,很适合用来做 restful 风格的设计

  • tornado Python Web 框架和异步网络库,它执行非阻塞 I/O , 没有对 REST API 的内置支持,但是用户可以手动实现。

  • FastAPI 快速构建,异步 IO,自带 Swagger 作为 API 文档,不用后续去内嵌 Swagger-Ui

我个人认为 FastAPI 是一个专门为 restful 风格设计,全面服务于 API 形式的 Web 后端框架。

FastAPI 官方定位

在 FastAPI 官方文档中,可以看到官方对 FastAPI 的定位:

  • 快速:非常高的性能,向 NodeJS 和 go 看齐(感谢 Starlette 和 Pydantic)

  • 快速编码:将功能开发速度提高约 200% 至 300%。

  • 错误更少:减少约 40% 的人为错误(开发人员)。* (FastAPI 内置很多 Python 高版本的语法,比如类型注释,typing 库等等,因此被要求的 Python 版本为 3.6+)

  • 简易:旨在易于使用和学习。减少阅读文档的时间。

  • 功能完善: 自带 Swagger 作为 API 文档

Framework Benchmarks

https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=fortune

上图可以看出,在高并发下 4 个框架的排名情况。单纯从性能出发,Web 框架是排在第一的。在选用框架的时候,性能是一方面,我们还要看业务上的需求和使用场景,最适合的才是最好的。

下面简单介绍一下 FastAPI 的一些用法和特性.

启动FastAPI

1 # pip install fastapi

2 # pip install uvicorn

3 from fastapi import FastAPI

4 app = FastAPI()

5 @app.get("/")

6 def read_root():

7 return {"Hello": "World"}

8 @app.get("/items/{item_id}")

0 def read_item(item_id: int, q: str = None):

10 return {"item_id": item_id, "q": q}

11 # uvicorn main:app # 启动

12 # uvicorn main:app --reload # 支持热更新

13 # uvicorn main:app --host 0.0.0.0 --port 8889 --reload # 自定义IP+端口

14

FastAPI 支持异步请求

1 from fastapi import FastAPI

2 app = FastAPI()

3 @app.get("/")

4 async def read_root():

5 return {"Hello": "World"}

6

7 @app.get("/items/{item_id}")

8 async def read_item(item_id: int, q: str = None):

9 return {"item_id": item_id, "q": q}

10

对 API 接口的支持性优异

设置根目录

1 # main.py

2 from fastapi import FastAPI

3 import users

4 app = FastAPI()

5 app.include_router(

6 users.router,

7 prefix="/fastapi/play/v1/users", # 路由前缀

8 tags=['users'] # 路由接口类别

9 )

10 # routers/users.py

11 from fastapi import FastAPI,APIRouter

12 from datetime import datetime,timedelta

13 router = APIRouter()

14 @router.get("/get/users/")

15 async def get_users():

16 return {

17 "desc":"Return to user list"

18 }

19

对路径参数进行限制

1 # 根据名字获取列表

2 @router.get("/get/user/{username}")

3 async def get_user_by_username(username :str):

4 """

5 - username: 用户名

6 """

7 return {

8 "desc":"this username is "+ username

9 }

10

对查询参数做限制

1 @router.get("/friends/")

2

3 # 设置为None的时候,默认不可以不填

4 async def get_friends_by_id(id :int=None):

5 for item in test_json['friends']:

6 if item['id'] == id:

7 return item

8 else:

9 return {

10 "desc": "no this id"

11 }

12 # 多参数请求查询

13 from typing import List

14 @router.get("/items/")

15 async def read_items(q: List[str] = Query(["foo", "bar"])):

16 query_items = {"q": q}

17 return query_items

18

设置请求体

1 # 设置请求实体

2 from pydantic import BaseModel,Field

3 class requestModel(BaseModel):

4 name :str

5 age : int = Field(..., gt=0, description="The age must be greater than zero")

6 desc: str

7

8

9 @router.post("/post/UserInfo/")

10 async def post_UserInfo(item: requestModel):

11 return item

12

请求体嵌套

1 from pydantic import BaseModel,Field

2 class levelInfoModel(BaseModel):

3 id:int = None

4 info: str = None

5

6 class ClassInfo(BaseModel):

7 id: int = None

8 name: str = Field(..., max_length=20, min_length=10,

9 description="The necessary fields")

10 desc: str = Field(None, max_length=30, min_length=10)

11 levelInfo: List[levelInfoModel]

12

13 class Config:

14 schema_extra = {

15 "example": {

16 "id": 1,

17 "name": "Foo",

18 "desc": "A very nice Item",

19 "levelInfo": [{

20 "id": 1,

21 "info": "一级"

22 }]

23 }

24 }

25

26 @router.post("/info/")

27 async def get_classInfo(item:ClassInfo):

28 return item

29

自定义响应码

1 @router.post("/items/", status_code=201)

2 async def create_item(name: str):

3 return {"name": name}

4

5 from fastapi import FastAPI, status

6

7

8 @app.post("/items/", status_code=status.HTTP_201_CREATED)

9 async def create_item(name: str):

10 return {"name": name}

11

依赖注入

1 from fastapi import Depends, FastAPI

2

3 async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):

4 return {"q": q, "skip": skip, "limit": limit}

5

6 @router.get("/items/")

7 async def read_items(commons: dict = Depends(common_parameters)):

8 return commons

9

10 @router.get("/users/")

11 async def read_users(commons: dict = Depends(common_parameters)):

12 return commons

13

FastAPI 框架支持多层嵌套依赖注入

登录demo

1 # 安装环境

2 mkdir fastapi-demo && cd fastapi-demo

3 virtualenv env

4 source env/bin/activate

5

6 # 下载项目

7 git clone https://github.com/hzjsea/BaseFastapi

8 cd BaseFastapi/

9 pip install -r requirements.txt

10 # 开启项目

11 uvicorn main:app --reload

12 # uvicorn main:app --host 0.0.0.0 --port 80 --reload

13

总结

FastAPI 的设计还是很符合 restful 的,在用到很多新技术的同时,也没有抛弃之前一些比较好用的内容,包括类型注释、依赖注入,Websocket,swaggerui 等等,以及其它的一些注释,比如 GraphQL。

数据库以及 orm 的选择

  • sqlalchemy 但是不支持异步,不过貌似可以扩展成异步。

  • tortoise-orm 类 django-orm 的异步 orm,不过正在起步过程中,有些功能还没有完成。

sqlalchemy实例

1 from typing import List

2 import databases

3 import sqlalchemy

4 from fastapi import FastAPI

5 from pydantic import BaseModel

6 # SQLAlchemy specific code, as with any other app

7 DATABASE_URL = "sqlite:///./test.db"

8 # DATABASE_URL = "postgresql://user:password@postgresserver/db"

9 database = databases.Database(DATABASE_URL)

10 metadata = sqlalchemy.MetaData()

11 notes = sqlalchemy.Table(

12 "notes",

13 metadata,

14 sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True),

15 sqlalchemy.Column("text", sqlalchemy.String),

16 sqlalchemy.Column("completed", sqlalchemy.Boolean),

17 )

18 engine = sqlalchemy.create_engine(

19 DATABASE_URL, connect_args={"check_same_thread": False}

20 )

21 metadata.create_all(engine)

22

23

24 class NoteIn(BaseModel):

25 text: str

26 completed: bool

27

28

29 class Note(BaseModel):

30 id: int

31 text: str

32 completed: bool

33

34

35 app = FastAPI()

36

37

38 @app.on_event("startup")

39 async def startup():

40 await database.connect()

41

42

43 @app.on_event("shutdown")

44 async def shutdown():

45 await database.disconnect()

46

47

48 @app.get("/notes/", response_model=List[Note])

49 async def read_notes():

50 query = notes.select()

51 return await database.fetch_all(query)

52

53

54 @app.post("/notes/", response_model=Note)

55 async def create_note(note: NoteIn):

56 query = notes.insert().values(text=note.text, completed=note.completed)

57 last_record_id = await database.execute(query)

58 return {**note.dict(), "id": last_record_id}

59

tortoise-orm实例

1 # main.py

2 from tortoise.contrib.fastapi import HTTPNotFoundError, register_tortois

3 # 创建的数据表

4 models = [

5 "app.Users.models",

6 "app.Face.models",

7 "app.Roster.models",

8 "app.Statistical.models",

9 "app.pay.models"

10 ]

11

12 register_tortoise(

13 app,

14 db_url="mysql://username:password@ip:port/yydb",

15 modules={"models": models},

16 generate_schemas=True,

17 add_exception_handlers=True,

18 )

19

20 # models.py

21 from tortoise import fields,models

22 from tortoise.contrib.pydantic import pydantic_queryset_creator

23 from pydantic import BaseModel

24 class RosterGroupTable(models.Model):

25 id = fields.IntField(pk=True)

26 phone = fields.CharField(max_length=20,blank=True,null=True)

27 name = fields.CharField(max_length=20)

28

29 class Meta:

30 db = "RosterGroupTable"

31

32 class RosterTabel(models.Model):

33 id = fields.IntField(pk=True)

34 phone = fields.CharField(max_length=20,blank=True,null=True)

35 name = fields.CharField(max_length=20)

36 group_id = fields.ForeignKeyField(model_name='models.RosterGroupTable',on_delete=fields.CASCADE,related_name="events",blank=True,null=True)

37

38 class Meta:

39 db = "RosterTabel"

40

41 RosterGroupTable_desc = pydantic_queryset_creator(RosterGroupTable)

42 RosterTabel_desc = pydantic_queryset_creator(RosterTabel)

43

44

45

46 # roster.py

47 @router.post("/roster_statistics/add")

48 async def roster_add_statics(*,item:RosterItem,token :str):

49 res = await RosterTabel.filter(id=item['memberId']).first()

50 if res:

51 await StatisticalRosterTable.create(

52 phone = current_user.Phone,

53 date = item['date'],

54 time = item['time'],

55 data = item['data'],

56 name_id_id = item['memberId'],

57 temp_type = item['tm_type'],

58 today_num = item['todayNum'],

59 group_id_id = res.group_id_id,

60 name = res.name

61 )

62 else:

63 return rp_faildMessage(msg="名单不存在",code=-1)

64 return rp_successMessage(msg="名单创建成功",code=0)

65

部署

dockerfile

1 #================================================================================

2 # 基于Python3.7的创建fastapi的dockerfile文件

3 # fastapi + Python3.7 + guvicorn

4 #================================================================================

5

6 FROM Python:3.7

7 LABEL version="v1"

8 description="fastapi"

9 maintainer="hzjsea"

10 using="fastapi & Python3.7 office image & gunicorn"

11 WORKDIR /root/code

12 COPY . .

13 RUN pip install -r requirements.txt

14 EXPOSE 8889

15 CMD ["gunicorn","-c","guvicorn.conf","main:app"]

16

supervisor项目托管

1 [program:webserver]

2 directory=/root/hzj/fastapi_play

3 command=/root/hzj/pro_env_all/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8888

4 autostart = true

5

部署完整示例

FastAPI官方提供了一个前后端分离项目完整示例

https://github.com/tiangolo/full-stack-fastapi-postgresql

文档及项目地址:

Documentation: https://fastapi.tiangolo.com

推荐阅读

从 301 跳转,聊聊边缘规则的那些小妙用

从新冠疫情出发,漫谈 Gossip 协议

最新文章

  1. Linux--shell脚本之正则表达式
  2. Windows Azure Web Site (13) Azure Web Site备份
  3. NDK开发之引用(局部引用,全局引用,虚全局引用)
  4. Linux信号(signal) 机制分析
  5. Load Balancing 折半枚举大法好啊
  6. Android上传文件之FTP
  7. js判断终端是手机还是电脑
  8. Test class should have exactly one public zero-argument constructor
  9. .net实现多重继承问题(virtual)
  10. #WEB安全基础 : HTTP协议 | 0x10 扩展HTTP报文结构概念和内容编码
  11. java不同的包下相同的类名的问题与解决办法
  12. 关于苹果手机微信端 position: fixed定位top导航栏问题
  13. JAVA核心技术I---JAVA基础知识(对象与类)
  14. filter过滤器实现验证跳转_返回验证结果
  15. 《学习opencv》笔记——矩阵和图像操作——cvAnd、cvAndS、cvAvg and cvAvgSdv
  16. 【PCA】
  17. hihocoder 1305 - 区间求差 - [hiho一下152周][区间问题]
  18. Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL
  19. 使用threejs点云秀出酷炫的图片效果(一)
  20. HDU 2044 一只小蜜蜂...(递推,Fibonacci)

热门文章

  1. kworkerds挖矿木马
  2. HDU-3033 I love sneakers! 题解
  3. x memory pool c语言 内存池
  4. 使用iText生成pdf文件
  5. 从零开始的Spring Boot(3、Spring Boot静态资源和文件上传)
  6. WEditor(元素定位工具)安装和定位界面元素
  7. Android学习笔记Tab代替ActionBar做的顶部导航
  8. [cpp]C++中的析构函数
  9. 定时任务Cron
  10. express 框架的使用方法