快速编码,功能完善。从启动到部署,实例详解异步 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 协议

三分钟了解 Python3 的异步 Web 框架 FastAPI的更多相关文章

  1. python 异步Web框架sanic

    我们继续学习Python异步编程,这里将介绍异步Web框架sanic,为什么不是tornado?从框架的易用性来说,Flask要远远比tornado简单,可惜flask不支持异步,而sanic就是类似 ...

  2. python3开发进阶-Web框架的前奏

    我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 1.自定义web框架 import socket ...

  3. 架构探险——第三章(搭建轻量级Java Web框架)

    解决的问题 servlet的数量会随业务功能的扩展而不断增加,我们有必要减少servlet的数量,交给controller处理,它负责调用service的相关方法,并将返回值放入request或res ...

  4. Python3之Django Web框架中间件???

    主要用来处理页面的登录验验.跨站请求伪造防御.日志记录.session设置,权限管理等

  5. python web应用--web框架(三)

    了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应. 但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL. 每一个URL ...

  6. Python面试-websocket及web框架

    一.Websocket 1. websocket概念 在讲websocket之前,我们先来看看ajax轮询和long poll的实现机制. A.  ajax轮询 ajax轮询的原理非常简单,让浏览器隔 ...

  7. “脚踢各大Python Web框架”,Sanic真有这能耐么?

    在Github上,Sanic第一句介绍语就是: "Sanic is a Flask-like Python 3.5+ web server that's written to go fast ...

  8. python笔记-19 javascript补充、web框架、django基础

    一.JavaScript的补充 1 正则表达式 1.1 test的使用 test 测试是否符合条件 返回true or false 1.2 exec的使用 exec 从字符串中截取匹配的字符 1.3 ...

  9. python各种web框架对比

    0 引言        python在web开发方面有着广泛的应用.鉴于各种各样的框架,对于开发者来说如何选择将成为一个问题.为此,我特此对比较常见的几种框架从性能.使用感受以及应用情况进行一个粗略的 ...

随机推荐

  1. Python--编码转换

    # -*- coding:gbk -*- # 即使设置文件编码为gbk,下方定义的字符串s1依旧为unicode # 获取默认编码格式 import sys print(sys.getdefaulte ...

  2. 多态的C语言实现版本

    #ifndef _51_2_H #define _51_2_H typedef void Demo; typedef void Derived; Demo* Demo_create(int i,int ...

  3. (四)MySQL条件查询(通配符、模糊查询)、排序查询、分组查询(单行、分组函数)

    一.条件查询 1.含义:前面学的基础查询可以查询一个或多个字段,如果需要的数据仅仅是其中的某一行或多行就用到了条件查询. 2.语法:(序号表示语句执行顺序) SELECT 字段名 ③ FROM 表名 ...

  4. RocketMQ系列(六)批量发送与过滤

    今天我们再来看看RocketMQ的另外两个小功能,消息的批量发送和过滤.这两个小功能提升了我们使用RocketMQ的效率. 批量发送 以前我们发送消息的时候,都是一个一个的发送,这样效率比较低下.能不 ...

  5. 开源一款超实用的 Dubbo 测试工具,已用半年,感觉很有feel~

    不知道你是否在工作中有遇到过类似情况: dubbo接口调试复杂,需要通过telnet命令或者通过consumer调用来触发. telnet语句参数格式复杂,每次编写都要小心谨慎,一旦出错又需重来. 复 ...

  6. 2.K8S的核心资源管理方法

    目录 1.1陈述式资源管理方法 1.1.1.管理名称空间资源 1.1.2.管理Deployment资源 1.1.3.管理Service资源 1.1.4.kubectl用法总结 1.2.声明式资源管理方 ...

  7. 对 JsonConvert 的认识太肤浅了,终于还是遇到了问题

    一:背景 1. 讲故事 在开始本文之前,真的好想做个问卷调查,到底有多少人和我一样,对 JsonConvert 的认识只局限在 SerializeObject 和 DeserializeObject ...

  8. 07.DRF-序列化

    Serializer序列化器 序列化器的作用: 进行数据的校验 对数据对象进行转换 一.定义Serializer 1.1 定义方法 Django REST framework中的Serializer使 ...

  9. Stream替代for-编码五分钟-划水五小时

    Stream替代for-编码五分钟-划水五小时 天空没有痕迹,风雨已在心中. 背景:使用Stream 流式操作取代俄罗斯式套娃的for循环,解放底层劳动密集型码畜的双手,使编码五分钟划水五小时,不再是 ...

  10. 从外包公司运作方式看EJB工作原理

    从来没用过EJB,然后进了家公司需要用,没办法,硬着头皮学吧.以下是个人学习体会,觉不好的话也不要吐槽了. 关于EJB的工作原理,你可以想象为一家公司(EJB容器),外包型的(服务接口), 公司内部有 ...