使用fastapi框架做标准程序接口
1 接口构建 文档
- 现代的方式定义类
我们使用第三方框架来构建api,这样顺便了解一个现代api需要做哪些事情,我们可以做哪些事情。
-
它支持 自动补全
类型检查 -
数据校验
在校验失败时自动生成清晰的错误信息 对多层嵌套的JSON对象依然执行校验
-
来自网络的请求的输入数据未python数据类型,包括
JSON 路径参数 查询参数 Cookies 请求头 表单 文件
-
转换输出的数据:转换Python数据类型为 网络传输的JSON数据
转换基础Python类型 str int float bool list 等 为 json datetime对象 转为 json对象 UUID对象转为 Json对象 数据库模型 自动转为Json对象
-
自动生成的交互式API文档,包括两种可选用户界面
Swagger UI ReDoc
-
基于开放标准
用于创建 API 的 OpenAPI 包含了路径操作,请求参数,请求体,安全性等的声明。
使用 JSON Schema (因为 OpenAPI 本身就是基于 JSON Schema 的)自动生成数据模型文档。
经过了缜密的研究后围绕这些标准而设计。并非狗尾续貂。
这也允许了在很多语言中自动生成客户端代码。
-
自动生成文档
交互式API文档以及具探索性web界面,因为该框架是基于OpenApi,所以具有很多选项,FastAPI默认自带两个交互式API文档 Swaggeer UI 可交互式操作,能在浏览器中直接调用和测试你的 API ReDoc
-
安装依赖
pip install fastapi pip install uvicorn pip install uvicorn[standard]
-
简单样本
uvicorn init_main:app --reload --host 0.0.0.0 --port 1999 2>&1 >fast.log &
1.1 校验 全部基于Python3.6的类型声明
Pydantic 的类型声明
此函数讲校验 item是否包括在路径中,其数据类型是否为 int 整型
:param item_id: 请求体 读取为JSON
:param item:
检查是否有必需属性 name 并且值为 str 类型
检查是否有必需属性 price 并且值为 float 类型
检查是否有可选属性 is_offer 并且值为 bool 类型
:return:
自动对JSON进行转换
1.2 安全性及身份验证
集成安全性和身份验证,杜绝数据库或数据模型渗透风险
HTTP基本认证
OAuth2 也就是JWT Token
API 密钥
请求头
查询参数
Cookies 等
加上来自 Starlette 包括 session cookie的所有安全特性。
所有的 这些都是可复用的工具和组件,轻松与你 的系统,数据仓库,关系型 以及NoSQL数据库集成。
1.3 依赖注入
openAPI 使用一个非常简单的,但是强大的 依赖注入系统。
依赖也可以有依赖,创建一个层级或者图依赖。
所有自动化处理都由框架完成。
所有依赖关系都可以从请求中获取数据,并且增加了路径操作约束和自动文档生成。
即使在依赖项中被定义的路径操作也会自动验证。
支持复杂的用户身份认证,数据库连接等。
不依赖数据库,前端等。 但是与它们集成很简单。
1.4 无限制 插件
或者说,导入并使用你需要的代码,
任何集成都被设计得易于使用 用依赖关系,你可以用与路径操作相同的结构和语法。
在两行代码中为你的应用创建一个插件。
1.5 Starlette特性
FastAPI 和 Starlette 完全兼容。 FastAPI实际上是 Starlette的一个子类。所以,如果你已经知道或者使用Starlette。
大部分功能以相同方式工作。
性能强大 与 NodeJS,GO相当
支持WebSocket
支持GraphQL
后台任务处理
Startup和shutdown操作
客户端基于 requests
支持Session和Cookie
100% 测试覆盖,和类型注释
兼容包括Pydantic的外部库,例如用数据库的ORMs,ODMs。
这表示你可以将从请求中获得相同对象直接传到数据库,因为所有验证都是自动的 。
你也可以将数据库中获取的对象直接传到 客户端。
openapi更简单
没有新的模式定义 micro-language需要学习
python types 与 pydantic 一样的使用方式
IED linter brain 适配
因为pydantic数据结构仅仅是你定义的类的实例
自动补全,linting,mypy,以及你的直觉应该可以和你验证的数据一起正常工作
更快
基准测试中,pydantic比其他测试库都快
复杂结构的校验
使用分层的Pydantic模型,python typing的List Dict等
验证器使我们能够简单清楚地将复杂数据模式定义,检查并记录为JSON Schema
你可以拥有深度的嵌套JSON对象并对它们进行验证和注释
可扩展
允许自定义数据类型或你可以用验证器对被装饰模型方法进行扩展 验证。
1.6 typing 容器类型list, tuple,set, dict 可以包含自定义子类型
items:List[str]
这表示变量 items 是一个 list,并且这个列表里的每一个元素都是 str"
如此编辑器 IDE将可以知道它是一个Str
声明 tuple 和 set的方法也一样
from typing import Set, Tuple
def process_items(items_t:Tuple[int, int, str], items_s:Set[bytes]):
return items_t, items_s
这表示:
变量 items_t是一个 tuple,其中的每个元素都是int类型
变量 items_s是一个set,其中每个元素都是bytes类型
字典声明
定义dict时,需要传入两个子类型,逗号分隔
第一个子类型 声明 dict 所有键
第二个子类型 声明 dict 所有值
from typing import Dict
def process_items(prices:Dict[str,float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)
变量prices 是一个dict:
这个 dict 的所有键为 str 类型,看着时字典内每个元素的名称
这个dict 的所有键为float 类型 可以看作是字典每个元素的价格
1.7 将类作为 参数的类型提示
将类声明为变量的类型。
假设你有一个名为 Perosn的类,拥有name属性。
class Person:
def __init__(self, name:str):
self.name = name
def get_person_name(one_person:Person):
return one_person.name
1.8 Pydantic模型
Pydantic 是一个用以执行是数据校验的Python库。
你可以将数据的结构声明为具有属性的类。
每个属性 都拥有类型。
接着你使用一些值来创建这个类的实例,这些值会被校验,并被转换为适当的类型。
在需要的情况下,返回一个包含所有数据的对象。
然后,你将获得这个对象的所有编辑器支持。
如1.7的例子。
1.9 FastAPI的 类型提示
类型提示的声明参数可以获得
编辑器支持
类型检查
OpenAPI 还使用这些类型声明
定义参数要求
声明对请求路径参数,查询参数,请求头,请求体,依赖等要求
转换数据
将来自请求的数据转换为需要的类型
校验数据
对于每一个请求
数据校验失败时自动生成错误信息 返回给客户端
使用OpenAPI记录API
然后用于自动生成交互式文档的用户界面。
综上:
通过使用标志的Python类型,只需要在一个地方声明(而不是添加更多的类,装饰器)FastAPI将为你完成很多工作。
1.10 模式
FastAPI使用定义API的OpenAPI标准将你的所有API转换为 [模式]
模式 是对事物的一种定义或描述,并非具体代码实现,只是抽象的描述。
API 模式
在这种场景下,OpenAPI是一种规定如何定义API模式的规范。
定义在OpenAPI模式将包括你的API路径,以及它们可能使用的参数等。
数据 模式
模式 这个术语 也可能指的是某些数据 比如JSON结构
它可以表示JSON的属性机器具有的数据类型,等
OpenAPI和JSON Schema
OpenAPI为你的API定义API模式。 该模式中包含了你的API发送和接收数据的定义(模式)
这些定义通过JSON数据模式标准 JSON Schema所生成。
如果你对元素的OpenAPI模式是什么形式 好奇,其实它只是一个自动生成包含所有 API描述的JSON。
访问本地服务的 open api 模式
http://127…0.0.1:1999/openapi.json
1.11 路由定义也可以使用多个协议支持的操作
你也可以使用其他的操作:
@app.get()
@app.post()
@app.put()
@app.delete()
以及更少见的:
@app.options()
@app.head()
@app.patch()
@app.trace()
2 绑定路由到路由组
2.1 路由URL中的查询参数
声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数 如下skip limit都是查询参数
返回json 格式,原始值 为字符串
http://192.168.30.131:1999/items/?skip=0&limit=1 # 限制查一个
http://192.168.30.131:1999/items/?skip=0&limit=10 # 限制查10个
:param skip: 对于的值为0
:param limit: 对于的值 10
参数原始值为 字符串,当你为它们声明一个Python类型时,
它们将转换为该类型并针对该类型进行校验
:return:
@app.get("/items/")
async def read_time(skip: int = 0, limit: int = 10):
return fake_items_db[skip:skip + limit]
当你为它们声明了python类型,它们将转换为该类型并针对该类型进行校验
应用于该路径参数的所有相同过程也适用于查询参数
编辑器支持
数据解析
数据校验
自动生成文档
- 默认值
查询参数可以有默认值
skip=0,limit=10 就是默认值
-
可选参数
使用设置None声明可选参数 -
多个路径参数和 查询参数
注意 如果 q 和 short 没有默认值 那就是必填查询参数
在这个例子中,有2个查询参数:
user_id: int, user_id 必须的int类型 路径参数。
item_id:str item_id 必须的str 类型 路径参数
q,一个必须的 没有默认值的str 类型参数。
short,一个可选的 bool 类型参数。
@app.get("/user/{user_id}/items/{item_id}") # 多个路径参数 和 查询参数
async def read_user_check(user_id: int, item_id:str, q: Optional[str], short:bool=False):
"""
q 是可选字符串参数 默认为 None
FastAPI可以分辨 参数 item_id 是路径参数,而q不是,因此q是查询参数
查询参数类型转换 声明不属于路径参数的其他函数参数时,它们将被自动解释为"查询字符串"参数
short: 是否排序
short=on 就是查询参数,FastAPI可以 识别 on,yes,True,true,1 为 python的True
> http://192.168.30.131:1999/user/1/items/2?q=ok&short=false
< response:
{"item_id":"2","own_id":1,"q":"ok","description":"This is an amazing item that has a long description"}
2.2 路径装饰器
-
传递参数给路径操作装饰器 ,即可轻松地配置路径操作、添加元数据。
tags 和 status_code 和 response_model
-
使用 status 快捷常量 HTTP_201_CREATED
-
使用 tags 为路径操作 添加标签,tag一般由 str组成的 list构成, 可以用于 api 接口分组
-
response_model 定义返回数据结构模型
@app.post("/items/create", response_model=Item, status_code=status.HTTP_201_CREATED, tags=[“items”])
async def create_item(item:Item):
return item -
summary 将在docs的 api右侧显示。
-
description参数 与接口的docs 描述一样的效果。
-
response_description 在docs中显示响应描述 。
-
deprecated=True 可以将路径操作标记为弃用,无需直接删除,
docs中将显示 此接口为灰色 POST /items/create Create an item
如果 description参数 与接口的docs 都设置了,那么只会显示一个描述 默认为路径中定义的,
-
路径装饰器 的高级设置
通过参数opeeration_id 设置 要使用的OpenAPO operationId
-
务必确保每个操作路径的 opeeration_id都是唯一的
-
注释不输出到 api docs
添加一个 \f (一个「换页」的转义字符)可以使 FastAPI 在那一位置截断用于 OpenAPI 的输出。
剩余部分不会出现在文档中,但是其他工具(比如 Sphinx)可以使用剩余部分。
2.3 路由响应
https://fastapi.tiangolo.com/zh/advanced/custom-response/
FastAPI 默认会使用 JSONResponse 返回响应
FastAPI 默认帮你把这个 item 放到 JSONResponse 中,又默认将其转换成了 dict
可以使用 Response重载,因为 JSONResponse 本身是一个 Response 的子类。
对于这些情况,在将数据传递给响应之前,你可以使用 jsonable_encoder 来转换你的数据。
-
使用api route的名称作为 operation_id
from controlers.apiroute import use_route_names_as_operation_ids
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
from starlette import status # 技术细节 于 从fastapi 导入 status是一样的
你也可以使用 from starlette.responses import JSONResponse。
- 出于方便,FastAPI 会提供与 starlette.responses 相同的 fastapi.responses 给开发者。
- 但是大多数可用的响应都直接来自 Starlette。
返回自定义response
return Response(content=data, media_type="application/xml")
XML响应
HTML响应
文档和重载Response
直接返回 HTMLResponse
def generate_html_response():
html_content = """
<html>
Look
</html>
"""
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()
response_class 中也传入了 HTMLResponse,FastAPI 会知道如何在 OpenAPI 和交互式文档中使用 text/html 将其文档化为 HTML。
通过返回函数 generate_html_response() 的调用结果,你已经返回一个重载 FastAPI 默认行为的 Response 对象
2.3.1 可用响应及自定义响应
使用 Response 来返回任何其他东西,甚至创建一个自定义的子类
content - 一个 str 或者 bytes。
status_code - 一个 int 类型的 HTTP 状态码。
headers - 一个由字符串组成的 dict。
media_type - 一个给出媒体类型的 str,比如 "text/html"。
纯文本响应
PlainTextResponse
UJSONResponse
response_class=UJSONResponse
在处理某些边缘情况时,ujson 不如 Python 的内置实现那么谨慎。
UJSONResponse 是一个使用 ujson 的可选 JSON 响应。
-
重定向 RedirectResponse
返回 HTTP 重定向。默认情况下使用 307 状态代码(临时重定向)。 return RedirectResponse("https://typer.tiangolo.com")
-
响应流 StreamingResponse
采用异步生成器或普通生成器/迭代器,然后流式传输响应主体 return StreamingResponse(fake_video_streamer())
-
对类似文件的对象使用 StreamingResponse
如果您有类似文件的对象(例如,由 open() 返回的对象), 则可以在 StreamingResponse 中将其返回。
包括许多与云存储,视频处理等交互的库
@app.get("/")
def main():
def iterfile(): # (1)
with open(some_file_path, mode="rb") as file_like: # (2)
yield from file_like # (3)
return StreamingResponse(iterfile(), media_type="video/mp4")
- 文件流FileResponse
异步传输文件作为响应。
与其他响应类型相比,接受不同的参数集进行实例化:
path - 要流式传输的文件的文件路径。
headers - 任何自定义响应头,传入字典类型。
media_type - 给出媒体类型的字符串。如果未设置,则文件名或路径将用于推断媒体类型。
filename - 如果给出,它将包含在响应的 Content-Disposition 中。
- 文件响应将包含适当的响应头
包含适当的 Content-Length,Last-Modified 和 ETag 的响应头。
@app.get("/")
async def main():
return FileResponse(some_file_path)
-
响应的其他文档
https://fastapi.tiangolo.com/zh/advanced/additional-responses/
3 CORS资源跨域共享和 资源访问限制
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
指浏览器中运行的前端拥有与后端通信的 JavaScript 代码,而后端处于与前端不同的「源」的情况
源是协议 http,https
域是 localhost,localhost.tiangolo.com 及端口 80 443 8080 的组合
因此 这些都是不同的源 因为协议和端口不同
http://localhost
https://localhost
http://localhost:8080
假设浏览器有一个前端运行在http://localhost:8080
js 正在尝试与 http://localhost 后端通信,因为我们没有指定端口默认为80
然后 浏览器向后端发送一个 http OPTIONS请求
如果后端发送适当的 headers 来授权来着这个不同源 8080 端口的通信
浏览器将允许前端 js向后端发送请求
为此后端必须有一个 允许的源 列表 并且包括 8080 端口
3.1 通配符
也可以使用 * 声明这个列表,表示全部都允许
但这仅允许某些类型通信,不包括所有涉及凭据的内容
如Cookies 以及那些使用Bearer令牌授权 headers
所以最好显式设置指定允许的源。
使用 CORSMiddlewware 步骤
导入
创建一个允许的源列表
将其作为 中间件添加到 FastAPI
也可以指定后端是否允许
凭证 授权headers cookies等
特定的HTTP方法
特定的HTTP headers 或使用通配符 * 允许所有headers
这个 CORSMiddleware 实现所使用的默认参数较为保守,所以你需要显式地启用特定的源、方法或者 headers,以便浏览器能够在跨域上下文中使用它们。
支持以下参数:
allow_origins - 一个允许跨域请求的源列表。例如 ['https://example.org', 'https://www.example.org']。你可以使用 ['*'] 允许任何源。
allow_origin_regex - 一个正则表达式字符串,匹配的源允许跨域请求。例如 'https://.*\.example\.org'。
allow_methods - 一个允许跨域请求的 HTTP 方法列表。默认为 ['GET']。你可以使用 ['*'] 来允许所有标准方法。
allow_headers - 一个允许跨域请求的 HTTP 请求头列表。默认为 []。你可以使用 ['*'] 允许所有的请求头。Accept、Accept-Language、Content-Language 以及 Content-Type 请求头总是允许 CORS 请求。
allow_credentials - 指示跨域请求支持 cookies。默认是 False。另外,允许凭证时 allow_origins 不能设定为 ['*'],必须指定源。
expose_headers - 指示可以被浏览器访问的响应头。默认为 []。
max_age - 设定浏览器缓存 CORS 响应的最长时间,单位是秒。默认为 600。
中间件响应两种特定类型的 HTTP 请求……
CORS预检请求
带有 Origin 和 Access-Control-Request-Method请求头的OPTIONS请求
这时,中间件将拦截传入的请求并进行响应
出于提供信息的目的返回一个使用了适当CORS headers的200 或 400 响应
简单请求
任何带有Origin请求头的请求。这时 中间件将像平常一样传递请求
在响应中包含适当的CORS headers
- 点赞
- 收藏
- 关注作者
评论(0)