当前位置: 首页 > news >正文

FastAPI教程II

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial

Cookie参数

定义Cookie参数与定义QueryPath参数一样。
具体步骤如下:

  • 导入Cookiefrom fastapi import Cookie
  • 声明Cookie参数,声明Cookie参数的方式与声明QueryPath参数相同,第一个值是默认值,还可以传递所有验证参数和注释参数
from typing import Annotatedfrom fastapi import Cookie, FastAPIapp = FastAPI()@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):return {"ads_id": ads_id}
  • 必须使用Cookie声明cookie参数,否则该参数会被解释成查询参数。

测试代码如下:

import requests# 设置你的 FastAPI 服务地址
base_url = "http://localhost:8000"# 发送带有 Cookie 的 GET 请求
def test_read_items_with_cookie():# 设置 Cookie 值cookies = {"ads_id": "your_ads_id_here"}# 发送 GET 请求,带上 Cookieresponse = requests.get(f"{base_url}/items/", cookies=cookies)# 打印响应内容print(response.json())# 执行测试
if __name__ == "__main__":test_read_items_with_cookie()

得到如下结果:

{'ads_id': 'your_ads_id_here'}

Header参数

定义Header参数的方式与定义QueryPathCookie参数相同。

步骤如下:

  • 导入Header
  • 声明Header参数:使用和PathQueryCookie一样的结构定义header参数,第一个值是默认值,还可以传递所有验证参数或注释参数
from typing import Annotatedfrom fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):return {"User-Agent": user_agent}
  • 必须使用Header声明header参数,否则该参数会被解释成查询参数。

自动转换

HeaderPathQueryCookie提供了更多功能。

大部分标准请求头用连字符分隔,即减号(-)

但是user-agent这样的变量在Python中是无效的。

因此,默认情况下,Header把参数名中的字符由下划线(_)改为连字符(-)来提取并存档请求头。

同时,HTTP的请求头不区分大小写,可以使用Python标准样式(即snake_case)进行声明。

因此,可以像在Python代码中一样使用user_agent,无需把首字母大写为User_Agent等形式。

如需禁用下划线自动转换为连字符,可以把Headerconvert_underscores参数设置为False

from typing import Annotatedfrom fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/")
async def read_items(strange_header: Annotated[str | None, Header(convert_underscores=False)] = None,
):return {"strange_header": strange_header}

注意,使用 convert_underscores = False 要慎重,有些 HTTP 代理和服务器不支持使用带有下划线的请求头。

重复的请求头

有时,可能需要接收重复的请求头。即同一个请求头有多个值。

类型声明中可以使用list定义多个请求头。

使用Pythonlist可以接收重复请求头所有的值。

例如,声明X-Token多次出现的请求头,可以写成这样:

from typing import Annotatedfrom fastapi import FastAPI, Headerapp = FastAPI()@app.get("/items/")
async def read_items(x_token: Annotated[list[str] | None, Header()] = None):return {"X-Token values": x_token}

与路径操作通信时,以下面的方式发送两个 HTTP 请求头:

X-Token: foo
X-Token: bar

响应结果是:

{"X-Token values": ["bar","foo"]
}

这部分有疑问,怎么发送两个同样的请求头,如果直接发送字典形式,那么同名的会覆盖。

响应模型

你可以在任意的路径操作中使用response_model参数来声明用于响应的模型:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 等等
from typing import Anyfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: str | None = Noneprice: floattax: float | None = Nonetags: list[str] = []@app.post("/items/", response_model=Item)
async def create_item(item: Item) -> Any:return item@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:return [{"name": "Portal Gun", "price": 42.0},{"name": "Plumbus", "price": 32.0},]

注意,response_model是【装饰器】方法(getpost等)的一个参数。不像之前的所有参数和请求体,它不属于路径操作函数

它接收的类型与你将为Pydantic模型属性所声明的类型相同,因此它可以是一个Pydantic模型,但也可以是一个由Pydantic模型组成的list,例如List[Item]

FastAPI将使用此response_model来:

  • 将输出数据转换为其声明的类型
  • 校验数据
  • 在OpenAPI的路径操作中为响应添加一个JSON Schema
  • 并在自动生成文档系统中使用

但最重要的是:

  • 会将输出数据限制在该模型定义内。

返回与输入相同的数据

现在我们声明一个UserIn模型,它将包含一个明文密码属性。

我们正在使用此模型声明输入数据,并使用同一模型声明输出数据:

from typing import Unionfrom fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: Union[str, None] = None# Don't do this in production!
@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:return user

现在,每当浏览器使用一个密码创建用户时,API都会在响应中返回相同的密码。

在这个案例中,这可能不算是问题,因为用户自己正在发送密码。

但是,如果我们在其他的路径操作中使用相同的模型,则可能会将用户的密码发送给每个客户端。

永远不要存储用户的明文密码,也不要在响应中发送密码。

添加输出模型

相反,我们可以创建一个有明文密码的输入模型和一个没有明文密码的输出模型:

from typing import Anyfrom fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: str | None = Noneclass UserOut(BaseModel):username: stremail: EmailStrfull_name: str | None = None@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:return user

这样,即使我们的路径操作函数将会返回包含密码的相同输入用户,我们已经将response_model声明为了不包含密码的UserOut模型,因此,FastAPI将会负责过滤掉未在输出模型中声明的所有数据(使用Pydantic)。

在文档中查看

当你查看自动化文档时,你可以检查输入模型和输出模型是否都具有自己的JSON Schema,并且两种模型都将在交互式API文档中使用。

响应模型编码参数

你的响应模型可以具有默认值,例如:

from typing import List, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: float = 10.5tags: List[str] = []items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):return items[item_id]
  • description: Union[str, None] = None 具有默认值 None
  • tax: float = 10.5 具有默认值 10.5.
  • tags: List[str] = [] 具有一个空列表作为默认值: [].

但如果它们并没有存储实际的值,你可能想从结果中忽略它们的默认值。

举个例子,当你在NoSQL数据库中保存了许多具有可选属性的模型,但你又不想发送充满默认值的很长的JSON响应。

使用response_model_exclude_unset参数

你可以设置路径操作装饰器的response_model_exclude_unset=True参数:

from typing import List, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: float = 10.5tags: List[str] = []items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):return items[item_id]

然后响应中将不会包含那些默认值,而是仅有实际设置的值。

因此,如果你向路径操作发送ID为Foo的商品的请求,则响应(不包括默认值)将为:

{"name": "Foo","price": 50.2
}

你还可以使用:

  • response_model_exclude_defaults=True
  • response_model_exclude_none=True
    参考 Pydantic 文档 中对 exclude_defaultsexclude_none 的描述。

response_model_includeresponse_model_exclude

你还可以使用路径操作装饰器的 response_model_includeresponse_model_exclude 参数。

它们接收一个由属性名称 str 组成的 set来包含(忽略其他的)或者排除(包含其他的)这些属性。

如果你只有一个 Pydantic 模型,并且想要从输出中移除一些数据,则可以使用这种快捷方法。
在这里插入图片描述

from typing import Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: float = 10.5items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},"baz": {"name": "Baz","description": "There goes my baz","price": 50.2,"tax": 10.5,},
}@app.get("/items/{item_id}/name",response_model=Item,response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):return items[item_id]@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):return items[item_id]

使用list而不是set

如果你忘记使用 set 而是使用 listtuple,FastAPI 仍会将其转换为 set 并且正常工作:

from typing import Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: float = 10.5items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},"baz": {"name": "Baz","description": "There goes my baz","price": 50.2,"tax": 10.5,},
}@app.get("/items/{item_id}/name",response_model=Item,response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):return items[item_id]@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):return items[item_id]

更多模型

多个关联模型这种情况很常见。

特别是用户模型,因为:

  • 输入模型应该含密码
  • 输出模型不应含密码
  • 数据库模型需要加密的密码

**千万不要存储用户的明文密码。始终存储可以进行验证的安全哈希值。

如果不了解这方面的知识,请参阅安全性中的章节,了解什么是密码哈希。**

多个模型

下面的代码展示了不同模型处理密码字段的方式,及使用位置的大致思路:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserIn(BaseModel):username: strpassword: stremail: EmailStrfull_name: str | None = Noneclass UserOut(BaseModel):username: stremail: EmailStrfull_name: str | None = Noneclass UserInDB(BaseModel):username: strhashed_password: stremail: EmailStrfull_name: str | None = Nonedef fake_password_hasher(raw_password: str):return "supersecret" + raw_passworddef fake_save_user(user_in: UserIn):hashed_password = fake_password_hasher(user_in.password)user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)print("User saved! ..not really")return user_in_db@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):user_saved = fake_save_user(user_in)return user_saved

**user_in_dict()简介

Pydantic的.dict()

user_in是类UserIn的Pydantic模型。

Pydantic模型支持.dict方法,能返回包含模型数据的字典。

因此,如果使用如下方式创建Pydantic对象user_in

user_in = UserIn(username="john", password="secret", email="john.doe@example.com")

就能以如下方式调用:

user_dict = user_in.dict()

现在,变量user_dict中的就是包含数据的字典(变量user_dict是字典,不是Pydantic模型对象)。

以如下方式调用:

print(user_dict)

输出的就是 Python 字典:

{'username': 'john','password': 'secret','email': 'john.doe@example.com','full_name': None,
}

解包dict

把字典user_dict**user_dict形式传递给函数(或类),Python会执行解包操作。它会把user_dict的键和值作为关键字参数直接传递。

因此,接着上面的user_dict继续编写如下代码:

UserInDB(**user_dict)

就会生成如下结果:

UserInDB(username="john",password="secret",email="john.doe@example.com",full_name=None,
)

或更精准,直接把可能会用到的内容与user_dict一起使用:

UserInDB(username = user_dict["username"],password = user_dict["password"],email = user_dict["email"],full_name = user_dict["full_name"],
)

用其它模型中的内容生成Pydantic模型

上例中,从user_in.dict()中得到了user_dict,下面的代码:

user_dict = user_in.dict()
UserInDB(**user_dict)

等效于:

UserInDB(**user_in.dict())

因为 user_in.dict() 是字典,在传递给 UserInDB 时,把 ** 加在 user_in.dict() 前,可以让 Python 进行解包。

这样,就可以用其它Pydantic模型中的数据生成Pydantic模型。

解包dict和更多关键字

接下来,继续添加关键字参数hashed_password=hashed_password,例如:

UserInDB(**user_in.dict(), hashed_password=hashed_password)

输出结果如下:

UserInDB(username = user_dict["username"],password = user_dict["password"],email = user_dict["email"],full_name = user_dict["full_name"],hashed_password = hashed_password,
)

减少重复

FastAPI的核心思想就是减少代码重复。

代码重复会导致bug、安全问题、代码失步等问题(更新了某个位置的代码,但没有同步更新其它位置的代码)。

上面的这些模型共享了大量数据,拥有重复的属性名和类型。

FastAPI可以做到更好。

声明UserBase模型作为其它模型的基类,然后,用该类衍生出继承其属性(类型声明、验证等)的子类。

所有数据转换、校验、文档等功能仍将正常运行。

这样,就可以仅声明模型之间的差异部分(具有明文的password、具有hashed_password以及不包括密码)。

通过这种方式,可以只声明模型之间的区别(分别包含明文密码、哈希密码,以及无密码的模型)。

from fastapi import FastAPI
from pydantic import BaseModel, EmailStrapp = FastAPI()class UserBase(BaseModel):username: stremail: EmailStrfull_name: str | None = Noneclass UserIn(UserBase):password: strclass UserOut(UserBase):passclass UserInDB(UserBase):hashed_password: strdef fake_password_hasher(raw_password: str):return "supersecret" + raw_passworddef fake_save_user(user_in: UserIn):hashed_password = fake_password_hasher(user_in.password)user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)print("User saved! ..not really")return user_in_db@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):user_saved = fake_save_user(user_in)return user_saved

Union或者anyOf

响应可以声明为两种类型的Union类型,即该响应可以是两种类型中的任意类型。

在OpenAPI中可以使用anyOf定义

为此,请使用Python标准类型提示typing.Union

from typing import Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class BaseItem(BaseModel):description: strtype: strclass CarItem(BaseItem):type: str = "car"class PlaneItem(BaseItem):type: str = "plane"size: intitems = {"item1": {"description": "All my friends drive a low rider", "type": "car"},"item2": {"description": "Music is my aeroplane, it's my aeroplane","type": "plane","size": 5,},
}@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):return items[item_id]

模型列表

使用同样的方式也可以声明由对象列表构成的响应。

为此,请使用标准的Python typing.List

from fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: stritems = [{"name": "Foo", "description": "There comes my hero"},{"name": "Red", "description": "It's my aeroplane"},
]@app.get("/items/", response_model=list[Item])
async def read_items():return items

任意dict构成的响应

任意的dict都能用于声明响应,只要声明键和值的类型,无需使用Pydantic模型。

事先不知道可用的字段/属性名时(Pydantic模型必须知道字段是什么),这种方式特别有用。

此时,可以使用typing.Dict

from fastapi import FastAPIapp = FastAPI()@app.get("/keyword-weights/", response_model=dict[str, float])
async def read_keyword_weights():return {"foo": 2.3, "bar": 3.4}

响应状态码

与指定响应模型的方式相同,在以下任意路径操作中,可以使用status_code参数声明用于响应的HTTP状态码:

  • @app.get()
  • @app.post()
  • @app.put()
  • @app.delete()
  • 等……
from fastapi import FastAPIapp = FastAPI()@app.post("/items/", status_code=201)
async def create_item(name: str):return {"name": name}

status_code参数接收表示HTTP状态码的数字。

它可以:

  • 在响应中返回状态码
  • 在OpenAPI概图(及用户界面)中存档

关于HTTP状态码

在HTTP协议中,发送3位数的数字状态码是相应的一部分。

这些状态码都具有便于识别的关联名称,但是重要的还是数字。

简言之:

  • 100及以上的状态码用于返回信息。这类状态码很少直接使用。具有这些状态码的响应不能包含响应体。
  • 200及以上的状态码用于表示成功。这些状态码是最常用的
    • 200是默认状态代码,表示一切正常
    • 201表示已创建,通常在数据库中创建新纪录后使用
    • 204是一种特殊的例子,表示无内容。该响应在没有为客户端返回内容时使用,因此,该响应不能包含响应体
  • 300及以上的状态码用于重定向。具有这些状态码的响应不一定包含响应体,但304未修改是个例外,该响应不得包含响应体。
  • 400及以上的状态码用于表示客户端错误。这些可能是第二常用的类型
    • 404,用于未找到响应
    • 对于来自客户端的一般错误,可以只使用400
  • 500及以上的状态码用于表示服务器端错误。几乎永远不会直接使用这些状态码。应用代码或服务器出现问题时,会自动返回这些状态代码

状态码及适用场景的详情,请参阅MDN 的 HTTP 状态码文档。

状态码名称快捷方式

再看下之前的例子:

from fastapi import FastAPIapp = FastAPI()@app.post("/items/", status_code=201)
async def create_item(name: str):return {"name": name}

201表示已创建的状态码。

但我们没有必要记住所有代码的含义。

可以使用fastapi.status中的快捷变量。

from fastapi import FastAPI, statusapp = FastAPI()@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):return {"name": name}

更改默认状态码

高级用户指南中,将介绍如何返回与在此声明的默认状态码不同的状态码。

表单数据

接收的不是JSON,而是表单字段时,要使用Form

要使用表单,需预先安装python-multipart
例如,pip install python-multipart

步骤如下:

  • 导入Form
  • 定义Form参数:创建表单参数的方式与BodyQuery一样
from fastapi import FastAPI, Formapp = FastAPI()@app.post("/login/")
async def login(username: str = Form(), password: str = Form()):return {"username": username}

例如,OAuth2规范的”密码流“模式规定要通过表单字段发送usernamepassword

该规范要求字段必须命名为usernamepassword,并通过表单字段发送,不能用JSON。

使用Form可以声明与Body(及QueryPathCookie)相同的元数据和验证。

Form是直接继承自Body的类

关于表单字段

在这里插入图片描述

请求文件

File用于定义客户端的上传文件。

因为上传文件以【表单数据】形式发送。所以接收上传文件,要预先安装python-multipart

步骤如下:

  • 导入File:从fastapi导入FileUploadFile
  • 定义File参数:创建文件File参数的方式与BodyForm一样
from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File()):return {"file_size": len(file)}@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):return {"filename": file.filename}

File是直接继承自Form的类

文件作为【表单数据】上传。

如果把路径操作函数参数的类型声明为bytes,FastAPI将以bytes形式读取和接收文件内容。

这种方式把文件的所有内容都存储在内存里,适用于小型文件。

不过,很多情况下,UploadFile更好用。

UploadFile的文件参数

定义文件参数时使用UploadFile

from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File()):return {"file_size": len(file)}@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):return {"filename": file.filename}

UploadFilebytes相比有很多优势:

  • 使用spooled文件:存储在内存的文件超出最大上限时,FastAPI会把文件存入磁盘;
  • 这种方式更适用于处理图像、视频、二进制文件等大型文件,好处是不会占用所有内存;
  • 可获取上传文件的元数据;
  • 自带file-likeasync接口;
  • 暴露的Python SpooledTemporaryFile对象,可直接传递给其他预期【file-like】对象的库。

UploadFile的属性如下:

  • filename:上传文件名字符串(str),例如,myimage.jpg
  • content_type:内容类型(MIME类型/媒体类型)字符串(str),例如,image/jpeg
  • file:SpooledTemporaryFile(file-like对象)。其实就是Python文件,可直接传递给其他预期file-like对象的函数或支持库。

UploadFile支持以下async方法,(使用内部SpooledTemporaryFile)可调用相应的文件方法。

  • write(data):把datastrbytes)写入文件;
  • read(size):按指定数量的字节或字符(size(int))读取文件内容;
  • seek(offset):移动至文件offset(int)字节处的位置;例如,await myfile.seek(0)移动到文件开头;执行await myfile.read()后,需再次读取已读取内容时,这种方法特别好用;
  • close():关闭文件。

因为上述方法都是async方法,要搭配【await】使用。

例如,在async路径操作函数内,要用以下方式读取文件内容:

contents = await myfile.read()

在普通def路径操作函数内,则可以直接访问UploadFile.file,例如:

contents = myfile.file.read()

在这里插入图片描述

什么是【表单数据】

与JSON不同,HTML表单(<form></form>)向服务器发送数据通常使用【特殊】的编码。

FastAPI要确保从正确的位置读取数据,而不是读取JSON。
在这里插入图片描述

可选文件上传

您可以通过使用标准类型注解并将None作为默认值的方式将一个文件参数设为可选:

from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):if not file:return {"message": "No file sent"}else:return {"file_size": len(file)}@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):if not file:return {"message": "No upload file sent"}else:return {"filename": file.filename}

带有额外元数据的UploadFIle

您也可以将File()UploadFile一起使用,例如,设置额外的元数据:

from fastapi import FastAPI, File, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File(description="A file read as bytes")):return {"file_size": len(file)}@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(description="A file read as UploadFile"),
):return {"filename": file.filename}

多文件上传

FastAPI支持同时上传多个文件。

可用同一个【表单字段】发送含多个文件的【表单数据】。

上传多个文件时,要声明含bytesUploadFile的列表(List):

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponseapp = FastAPI()@app.post("/files/")
async def create_files(files: list[bytes] = File()):return {"file_sizes": [len(file) for file in files]}@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):return {"filenames": [file.filename for file in files]}@app.get("/")
async def main():content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>"""return HTMLResponse(content=content)

接收的也是含bytesUploadFile的列表(list)。

带有额外元数据的多文件上传

和之前的方式一样,您可以为File()设置额外参数,即使是UploadFile

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponseapp = FastAPI()@app.post("/files/")
async def create_files(files: list[bytes] = File(description="Multiple files as bytes"),
):return {"file_sizes": [len(file) for file in files]}@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):return {"filenames": [file.filename for file in files]}@app.get("/")
async def main():content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>"""return HTMLResponse(content=content)

请求表单与文件

FastAPI支持同时使用FileForm定义文件和表单字段。

  • 导入FileForm
  • 定义FileForm参数
from fastapi import FastAPI, File, Form, UploadFileapp = FastAPI()@app.post("/files/")
async def create_file(file: bytes = File(), fileb: UploadFile = File(), token: str = Form()
):return {"file_size": len(file),"token": token,"fileb_content_type": fileb.content_type,}

文件和表单字段作为表单数据上传与接收。

声明文件可以使用bytesUploadFile
在这里插入图片描述

处理错误

某些情况下,需要向客户端返回错误提示。

这里所谓的客户端包括前端浏览器、其他应用程序、物联网设备等。

需要向客户端返回错误提示的场景主要如下:

  • 客户端没有执行操作的权限
  • 客户端没有访问资源的权限
  • 客户端要访问的项目不存在
  • 等等…

遇到这些情况时,通常要返回4XX(400至499)HTTP状态码

使用HTTPException

向客户端返回HTTP错误响应,可以使用HTTPException

步骤如下:

  • 导入HTTPException
  • 触发HTTPExceptionHTTPException是额外包含了和API有关数据的常规Python异常。因为是Python异常,所以不能return,只能raise。如在调用路径操作函数里的工具函数时,触发了HTTPException,FastAPI就不再继续执行路径操作函数中的后续代码,而是立即终止请求,并把HTTPException的HTTP错误发送给客户端。
  • 本例中,客户端用ID请求的item不存在时,触发状态码为404的异常:
from fastapi import FastAPI, HTTPExceptionapp = FastAPI()items = {"foo": "The Foo Wrestlers"}@app.get("/items/{item_id}")
async def read_item(item_id: str):if item_id not in items:raise HTTPException(status_code=404, detail="Item not found")return {"item": items[item_id]}

响应结果

请求为http://example.com/items/fooitem_id 为 「foo」)时,客户端会接收到HTTP状态码-200及如下JSON响应结果:

{"item": "The Foo Wrestlers"
}

但如果客户端请求 http://example.com/items/baritem_idbar」 不存在时),则会接收到 HTTP 状态码 - 404(「未找到」错误)及如下 JSON 响应结果:

{"detail": "Item not found"
}

添加自定义响应头

有些场景下要为HTTP错误添加自定义响应头。例如,出于某些方面的安全需要。

一般情况下可能不会需要在代码中直接使用响应头。

但对于某些高级应用场景,还是需要添加自定义响应头:

from fastapi import FastAPI, HTTPExceptionapp = FastAPI()items = {"foo": "The Foo Wrestlers"}@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):if item_id not in items:raise HTTPException(status_code=404,detail="Item not found",headers={"X-Error": "There goes my error"},)return {"item": items[item_id]}

安装自定义异常处理器

添加自定义处理器,要使用 Starlette 的异常工具。

假设要触发的自定义异常叫做UnicornException

且需要FastAPI实现全局处理该异常。

此时,可以用@app.exception_handler()添加自定义异常控制器:

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponseclass UnicornException(Exception):def __init__(self, name: str):self.name = nameapp = FastAPI()@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):return JSONResponse(status_code=418,content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},)@app.get("/unicorns/{name}")
async def read_unicorn(name: str):if name == "yolo":raise UnicornException(name=name)return {"unicorn_name": name}

请求/unicorns/yolo时,路径操作会触发UnicornException

但该异常将会被unicorn_exception_handler处理。

接收到的错误信息清晰明了,HTTP状态码为418,JSON内容如下:

{"message": "Oops! yolo did something. There goes a rainbow..."}

覆盖默认异常处理器

FastAPI自带了一些默认异常处理器。

触发HTTPException或请求无效数据时,这些处理器返回默认的JSON响应结果。

不过,也可以使用自定义处理器覆盖默认异常处理器。

覆盖请求验证异常

请求中包含无效数据,FastAPI内部会触发RequestValidationError

该异常也内置了默认异常处理器。

覆盖默认异常处理器时需要导入RequestValidationError,并用@app.excption_handler(RequestValidationError)装饰异常处理器。

这样,异常处理器就可以接收Request与异常。

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPExceptionapp = FastAPI()@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):return PlainTextResponse(str(exc.detail), status_code=exc.status_code)@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):return PlainTextResponse(str(exc), status_code=400)@app.get("/items/{item_id}")
async def read_item(item_id: int):if item_id == 3:raise HTTPException(status_code=418, detail="Nope! I don't like 3.")return {"item_id": item_id}

访问/items/foo,可以看到默认的JSON错误信息:

{"detail": [{"loc": ["path","item_id"],"msg": "value is not a valid integer","type": "type_error.integer"}]
}

被替换为了以下文本格式的错误信息:

1 validation error
path -> item_idvalue is not a valid integer (type=type_error.integer)

路径操作配置

路径操作装饰器支持多种配置参数。

status_code状态码

status_code 用于定义路径操作响应中的 HTTP 状态码。

可以直接传递 int 代码, 比如 404

如果记不住数字码的涵义,也可以用 status 的快捷常量:

from typing import Set, Unionfrom fastapi import FastAPI, status
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: Set[str] = set()@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):return item

tags参数

tags参数的值是由str组成的list(一般只有一个str),tags用于为路径操作添加标签:

from typing import Set, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: Set[str] = set()@app.post("/items/", response_model=Item, tags=["items"])
async def create_item(item: Item):return item@app.get("/items/", tags=["items"])
async def read_items():return [{"name": "Foo", "price": 42}]@app.get("/users/", tags=["users"])
async def read_users():return [{"username": "johndoe"}]

OpenAPI 概图会自动添加标签,供 API 文档接口使用:
在这里插入图片描述

summarydescription参数

路径装饰器还支持summarydescription这两个参数:

from typing import Set, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: Set[str] = set()@app.post("/items/",response_model=Item,summary="Create an item",description="Create an item with all the information, name, description, price, tax and a set of unique tags",
)
async def create_item(item: Item):return item

文档字符串(docstring

描述内容比较长且占用多行时,可以在函数的docstring中声明路径操作的描述,FastAPI支持从文档字符串中读取描述内容。

文档字符串支持Markdown,能正确解析和显示Markdown的内容,但要注意文档字符串的缩进。

from typing import Set, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: Set[str] = set()@app.post("/items/", response_model=Item, summary="Create an item")
async def create_item(item: Item):"""Create an item with all the information:- **name**: each item must have a name- **description**: a long description- **price**: required- **tax**: if the item doesn't have tax, you can omit this- **tags**: a set of unique tag strings for this item"""return item

下图为 Markdown 文本在 API 文档中的显示效果:
在这里插入图片描述

响应描述

response_description参数用于定义响应的描述说明:

from typing import Set, Unionfrom fastapi import FastAPI
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: strdescription: Union[str, None] = Noneprice: floattax: Union[float, None] = Nonetags: Set[str] = set()@app.post("/items/",response_model=Item,summary="Create an item",response_description="The created item",
)
async def create_item(item: Item):"""Create an item with all the information:- **name**: each item must have a name- **description**: a long description- **price**: required- **tax**: if the item doesn't have tax, you can omit this- **tags**: a set of unique tag strings for this item"""return item

弃用路径操作

deprecated参数可以把路径操作标记为弃用,无需直接删除:

from fastapi import FastAPIapp = FastAPI()@app.get("/items/", tags=["items"])
async def read_items():return [{"name": "Foo", "price": 42}]@app.get("/users/", tags=["users"])
async def read_users():return [{"username": "johndoe"}]@app.get("/elements/", tags=["items"], deprecated=True)
async def read_elements():return [{"item_id": "Foo"}]

JSON兼容编码器

在某些情况下,您可能需要将数据类型(如Pydantic模型)转换为与JSON兼容的数据类型(如dictlist)。

比如,如果您需要将其存储在数据库中。

对于这种要求,FastAPI提供了jsonable_encoder()函数。

使用jsonable_encoder

让我们假设你有一个数据库名为fake_db,它只能接收与JSON兼容的数据。

例如,它不接收datatime这类的对象,因为这些对象与JSON不兼容。

因此,datatime对象必须将转换为包含ISO格式化的str类型对象。

同样,这个数据库也不会接收Pydantic模型(带有属性的对象),而只接受dict

对此你可以使用jsonable_encoder

它接收一个对象,比如Pydantic模型,并会返回一个JSON兼容的版本:

from datetime import datetimefrom fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModelfake_db = {}class Item(BaseModel):title: strtimestamp: datetimedescription: str | None = Noneapp = FastAPI()@app.put("/items/{id}")
def update_item(id: str, item: Item):json_compatible_item_data = jsonable_encoder(item)fake_db[id] = json_compatible_item_data

在这个例子中,它将Pydantic模型转换为dict,并将datetime转换为str

调用它的结果后就可以使用Python标准编码中的json.dumps()。

这个操作不会返回一个包含JSON格式(作为字符串)数据的庞大的str。它将返回一个Python标准数据结构(例如dict),其值和子值都与JSON兼容。

请求体-更新数据

PUT更新数据

更新数据请用HTTP PUT操作。

把输入数据转换为以JSON格式存储的数据(比如,使用NoSQL数据库时),可以使用jsonable_encoder。例如,把datetime转换为str

from typing import List, Unionfrom fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: Union[str, None] = Nonedescription: Union[str, None] = Noneprice: Union[float, None] = Nonetax: float = 10.5tags: List[str] = []items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):return items[item_id]@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):update_item_encoded = jsonable_encoder(item)items[item_id] = update_item_encodedreturn update_item_encoded

PUT用于接收替换现有数据的数据。

PUT把数据项bar更新为以下内容时:

{"name": "Barz","price": 3,"description": None,
}

因为上述数据未包含已存储的属性 "tax": 20.2,新的输入模型会把 "tax": 10.5 作为默认值。

因此,本次操作把 tax 的值「更新」为 10.5。

PATCH进行部分更新(用PUT也无妨)

  • 使用Pydantic的exclude_unset参数:更新部分数据时,可以在Pydantic模型的.dict()中使用exclude_unset参数。比如item.dict(exclude_unset=True)。这段代码生成的dict只包含创建item模型时显式设置的数据,而不包括默认值。然后再用它生成一个只含已设置(在请求中发送)数据,且省略了默认值的dict
  • 使用Pydantic的update参数:接下来,用.copy()为已有模型创建调用update参数的副本,该参数为包含更新数据的dict。例如,stored_item_model.copy(update=update_data)
from typing import List, Unionfrom fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModelapp = FastAPI()class Item(BaseModel):name: Union[str, None] = Nonedescription: Union[str, None] = Noneprice: Union[float, None] = Nonetax: float = 10.5tags: List[str] = []items = {"foo": {"name": "Foo", "price": 50.2},"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):return items[item_id]@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):stored_item_data = items[item_id]stored_item_model = Item(**stored_item_data)update_data = item.dict(exclude_unset=True)updated_item = stored_item_model.copy(update=update_data)items[item_id] = jsonable_encoder(updated_item)return updated_item

更新部分数据小结
简而言之,更新部分数据应:

  • 使用PATCH而不是PUT(可选,也可以用PUT);
  • 提取存储的数据;
  • 把数据放入Pydantic模型;
  • 生成不含输入模型默认值的dict(使用exclude_unset参数);只更新用户设置过的值,不用模型中的默认值覆盖已存储过的值。
  • 为已存储的模型创建副本,用接收的数据更新其属性(使用update参数)。
  • 把模型副本转换为可存入数据库的形式(比如,使用jsonable_encoder)。把这种方式与Pydantic模型的.dict()方法类似,但能确保把值转换为适配JSON的数据类型,例如,把datetime转换为str
  • 把数据保存至数据库;
  • 返回更新后的模型。

相关文章:

FastAPI教程II

本文参考FastAPI教程https://fastapi.tiangolo.com/zh/tutorial Cookie参数 定义Cookie参数与定义Query和Path参数一样。 具体步骤如下&#xff1a; 导入Cookie&#xff1a;from fastapi import Cookie声明Cookie参数&#xff0c;声明Cookie参数的方式与声明Query和Path参数…...

Facebook的投流技巧有哪些?

相信大家都知道Facebook拥有着巨大的用户群体和高转化率&#xff0c;在国外社交推广中的影响不言而喻。但随着Facebook广告的竞争越来越激烈&#xff0c;在Facebook广告上获得高投资回报率也变得越来越困难。IPIDEA代理IP今天就教大家如何在Facebook上投放广告的技巧&#xff0…...

Spring Boot 中的微服务监控与管理

微服务的概述 微服务架构的优点和挑战 优点: 灵活性和可扩展性:微服务架构允许每个服务单独部署和扩展,这使得系统可以更灵活地适应不同的业务需求和负载变化。 使团队更加聚焦:每个微服务都有明确的职责,这使得开发团队可以更加聚焦,专注于开发他们的服务。 技术和框…...

【计算机网络】期末复习(1)模拟卷

一、选择题 1. 电路交换的三个阶段是建立连接、()和释放连接 A. Hello包探测 B. 通信 C. 二次握手 D. 总线连接 2. 一下哪个协议不属于C/S模式() A. SNMP…...

【软件工程中的演化模型及其优缺点】

文章目录 1. 增量模型什么是增量模型&#xff1f;优点缺点 2. 增量-迭代模型什么是增量-迭代模型&#xff1f;优点缺点 3. 螺旋模型什么是螺旋模型&#xff1f;优点缺点 1. 增量模型 什么是增量模型&#xff1f; 增量模型是一种逐步增加功能和特性的开发方法。项目被划分为多…...

Oracle 数据库详解:概念、结构、使用场景与常用命令

1. 引言 Oracle 数据库作为全球领先的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;在企业级应用中占据了重要地位。本文将详细介绍Oracle数据库的核心概念、架构、常用操作及其广泛的使用场景&#xff0c;旨在为读者提供全面而深入的理解。 2. Oracle 数据…...

FreeRTOS的裁剪与移植

文章目录 1 FreeRTOS裁剪与移植1.1 FreeRTOS基础1.1.1 RTOS与GPOS1.1.2 堆与栈1.1.3 FreeRTOS核心文件1.1.4 FreeRTOS语法 1.2 FreeRTOS移植和裁剪 1 FreeRTOS裁剪与移植 1.1 FreeRTOS基础 1.1.1 RTOS与GPOS ​ 实时操作系统&#xff08;RTOS&#xff09;&#xff1a;是指当…...

能求一个数字的字符数量的程序

目录 开头程序程序的流程图程序输入与打印的效果例1输入输出 例2输入输出 关于这个程序的一些实用内容结尾 开头 大家好&#xff0c;我叫这是我58&#xff0c;今天&#xff0c;我们先来看一下下面的程序。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>…...

PTA-线性表实验(JAVA)

题目1&#xff1a;Josephus环的问题及算法 【实验内容】 编程实现如下功能&#xff1a; 题意说明&#xff1a;古代某法官要判决n个犯人的死刑&#xff0c;他有一条荒唐的法律&#xff0c;将犯人站成一个圆圈&#xff0c;从第start个犯人开始数起&#xff0c;每数到第distance的…...

LeetCode:494. 目标和

题目 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘’ 或 ‘-’ &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式 &#xff1a; 例如&#xff0c;nums [2, 1] &#xff0c;可以在 2 之前添加 ‘’ &#xff0c;在 1 之前添…...

HarmonyOS Next开发学习手册——选项卡 (Tabs)

当页面信息较多时&#xff0c;为了让用户能够聚焦于当前显示的内容&#xff0c;需要对页面内容进行分类&#xff0c;提高页面空间利用率。 Tabs 组件可以在一个页面内快速实现视图内容的切换&#xff0c;一方面提升查找信息的效率&#xff0c;另一方面精简用户单次获取到的信息…...

LeetCode2710.移除字符串中的尾随零

cpp class Solution { public:string removeTrailingZeros(string num) {int flag 0;string s num;int size num.length();for (int i num.length() - 1; i > 0; i--) {if (num[i] ! 0)break;if (num[i] 0) {size--;}}s.resize(size);return s;} };...

PPT录屏怎么录?PPT录屏,3种方法简单操作

在数字化时代&#xff0c;PPT已经成为我们日常工作、学习和生活中不可或缺的一部分。无论是商务报告、教学课件还是产品展示&#xff0c;PPT都能帮助我们更加生动、直观地传递信息。然而&#xff0c;有时候我们会面临PPT录屏怎么录的问题。这时&#xff0c;一个好的PPT录屏功能…...

HarmonyOS开发:应用完整性校验

简介 为了确保应用的完整性和来源可靠&#xff0c;OpenHarmony需要对应用进行签名和验签。 应用开发阶段&#xff1a; 开发者完成开发并生成安装包后&#xff0c;需要开发者对安装包进行签名&#xff0c;以证明安装包发布到设备的过程中没有被篡改。OpenHarmony的应用完整性校…...

【MySQL基础篇】SQL指令:DQL及DCL

1、DQL DQL - 介绍 DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据表中的记录。&#xff08;在MySQL中应用是最为广泛的&#xff09; 查询关键字&#xff1a;SELECT DQL - 语法 SELECT 字段列表 FROM 表名列表 WHER…...

[C++][设计模式][适配器模式]详细讲解

目录 1.动机2.模式定义3.要点总结4.代码感受 1.动机 在软件系统中&#xff0c;由于应用环境的变化&#xff0c;常常需要将”一些现存的对象“放在新的环境中应用&#xff0c;但是新环境要求的接口是这些现存对象所不满足如何应对这些”迁移的变化“&#xff1f;如何既能利用现…...

8080时序驱动TFT显示屏 驱动IC GC9307

8080时序总共有控制线 CS片选线 DC(命令数据控制线) RD读控制线 WR写控制线 和N条数据线。 控制底层代码如下; 写读代码,读的代码反过来就行 inline void TFT8080WriteDat(unsigned char dat) {CS_L;//开始片选DC_H;//写数据 // RD_H;//禁止读WR_H;//禁止写WR_L;//写入…...

K8S 集群节点缩容

环境说明&#xff1a; 主机名IP地址CPU/内存角色K8S版本Docker版本k8s231192.168.99.2312C4Gmaster1.23.1720.10.24k8s232192.168.99.2322C4Gwoker1.23.1720.10.24k8s233&#xff08;需下线&#xff09;192.168.99.2332C4Gwoker1.23.1720.10.24 1. K8S 集群节点缩容 当集群中有…...

Web-HTML-事件

1 需求 2 语法 3 示例 4 参考资料 HTML 事件 | 菜鸟教程...

Installed Build Tools revision xxx is corrupted. Remove and install again 解决

1.在buildTools文件下找到对应的sdk版本&#xff0c;首先将版本对应目录下的d8.bat改名为dx.bat。 2.在lib文件下将d8.jar改名为dx.jar。 3.重新编译工程即可...

AI 与 Python 实战干货:基于深度学习的图像识别

《AI 与 Python 实战干货&#xff1a;基于深度学习的图像识别》 今天咱不啰嗦&#xff0c;直接上干货&#xff01; 在 AI 领域&#xff0c;特别是图像识别方面&#xff0c;Python 简直是一把利器。咱就以手写数字识别为例&#xff0c;来看看怎么用 Python 实现一个深度学习模…...

万字长文详解数据结构:树 | 第6章 | Java版大话数据结构 | 二叉树 | 哈夫曼树 | 二叉树遍历 | 构造二叉树 | LeetCode练习

&#x1f4cc;本篇分享的大话数据结构中&#x1f384;树&#x1f384;这一章的知识点&#xff0c;在此基础上&#xff0c;增加了练习题帮助大家理解一些重要的概念✅&#xff1b;同时&#xff0c;由于原文使用的C语言代码&#xff0c;不利于学习Java语言的同学实践&#xff0c;…...

NPOI入门指南:轻松操作Excel文件的.NET库

目录 引言 一、NPOI概述 二、NPOI的主要用途 三、安装NPOI库 四、NPOI基本使用 六、性能优化和内存管理 七、常见问题与解决方案 八、结论 附录 引言 Excel文件作为数据处理的重要工具&#xff0c;广泛应用于各种场景。然而&#xff0c;在没有安装Microsoft Office的…...

【高性能服务器】服务器概述

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 ​ 服务器概述 服…...

003 SSM框架整合

文章目录 整合web.xmlapplicationContext-dao.xmlapplicationContext-service.xmlspringmvc.xmldb.propertieslog4j.propertiespom.xml 测试sqlItemController.javaItemMapper.javaItem.javaItemExample.javaItemService.javaItemServiceImpl.javaItemMapper.xml 整合 将工程的…...

web刷题记录(7)

[HDCTF 2023]SearchMaster 打开环境&#xff0c;首先的提示信息就是告诉我们&#xff0c;可以用post传参的方式来传入参数data 首先考虑的还是rce&#xff0c;但是这里发现&#xff0c;不管输入那种命令&#xff0c;它都会直接显示在中间的那一小行里面&#xff0c;而实际的命令…...

【单片机毕业设计选题24037】-基于STM32的电力系统电力参数无线监控系统

系统功能: 系统上电后&#xff0c;OLED显示“欢迎使用电力监控系统请稍后”&#xff0c;两秒后显示“Waiting..”等待ESP8266初始化完成&#xff0c; ESP8266初始化成功后进入正常页面显示&#xff0c; 第一行显示电压值&#xff08;单位V&#xff09; 第二行显示电流值&am…...

Python使用彩虹表来尝试对MD5哈希进行破解

MD5是一种散列算法&#xff0c;它是不可逆的&#xff0c;无法直接解密。它的主要作用是将输入数据进行散列&#xff0c;生成一个固定长度的唯一哈希值。 然而&#xff0c;可以使用预先计算好的MD5哈希值的彩虹表&#xff08;Rainbow Table&#xff09;来尝试对MD5进行破解。彩…...

数据恢复篇: 如何在数据丢失后恢复照片

数据丢失的情况并不少见。如果您曾经遇到过图像丢失的情况&#xff0c;您可能想过照片恢复工具是如何工作的&#xff1f;可能会丢失多少数据图像&#xff1f;即使是断电也可能导致照片和媒体文件丢失。 话虽如此&#xff0c;如果你认为删除的照片无法恢复&#xff0c;那你就错…...

c++ 引用第三方库

文章目录 背景编写cmake代码里引用测试 背景 遇到一个c项目&#xff0c;想跑一些示例。了解下如何直接引用第三方库。 编写cmake 项目结构 myprojectincludexx.hmain.cppCMakeLists.txt CMakeLists.txt cmake_minimum_required(VERSION 3.28) project(velox_demo)set(CM…...

[数据集][目标检测]猪只状态吃喝睡站检测数据集VOC+YOLO格式530张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;530 标注数量(xml文件个数)&#xff1a;530 标注数量(txt文件个数)&#xff1a;530 标注类别…...

Redis中设置验证码

限制一分钟内最多发送5次&#xff0c;且每次有效时间是5分钟&#xff01; String 发送验证码(phoneNumber) {key "shortMsg:limit:" phoneNumber;// 设置过期时间为 1 分钟&#xff08;60 秒&#xff09;// 使⽤ NX&#xff0c;只在不存在 key 时才能设置成功bool…...

使用hadoop进行数据分析

Hadoop是一个开源框架&#xff0c;它允许分布式处理大数据集群上的大量数据。Hadoop由两个主要部分组成&#xff1a;HDFS&#xff08;Hadoop分布式文件系统&#xff09;和MapReduce。以下是使用Hadoop进行数据分析的基本步骤&#xff1a; 数据准备&#xff1a; 将数据存储在HDF…...

架构师篇-7、企业安全架构设计及实践

摘要&#xff1a; 认识企业安全架构企业安全案例分析及实践 内容&#xff1a; 为什么做企业安全架构怎么做好安全架构设计案例实践分析&随堂练 为什么要做企业安全架构 安全是麻烦制造者&#xff1f; 整天提安全需求增加开发工作增加运维要求增加不确定性延后业务上线…...

递归算法~快速排序、归并排序

递归排序是一种基于分治法的排序算法&#xff0c;最典型的例子就是快速排序和归并排序。这两种算法都利用递归将问题分解成更小的子问题&#xff0c;然后将子问题的解合并以得到原始问题的解。 1、快速排序&#xff08;Quick Sort&#xff09; 快速排序的基本思想是选择一个基…...

DarkGPT:基于GPT-4-200k设计的人工智能OSINT助手

关于DarkGPT DarkGPT是一款功能强大的人工智能安全助手&#xff0c;该工具基于GPT-4-200k设计并实现其功能&#xff0c;可以帮助广大研究人员针对泄露数据库进行安全分析和数据查询相关的OSINT操作。 工具要求 openai1.13.3 requests python-dotenv pydantic1.10.12 工具安装 …...

RAG 检索增强生成有效评估

我们将介绍RAG(检索增强生成)的评估工作流程 RAG工作流程的部分 数据集 这里是我们将要使用的LCEL (LangChain Expression Language)相关问题的数据集。 这个数据集是在LangSmith UI中使用csv上传创建的: https://smith.langchain.com/public/730d833b-74da-43e2-a614-4e2ca…...

Day38:LeedCode 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…...

sqlalchemy分页查询

sqlalchemy分页查询 在SQLAlchemy中,可以使用limit和offset方法实现分页查询 from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine from models import MyModel # 假设MyModel是你定义的模型# 连接数据库 engine = create_engine(sqlite:///myd…...

Java--常用类APl(复习总结)

前言: Java是一种强大而灵活的编程语言&#xff0c;具有广泛的应用范围&#xff0c;从桌面应用程序到企业级应用程序都能够使用Java进行开发。在Java的编程过程中&#xff0c;使用标准类库是非常重要的&#xff0c;因为标准类库提供了丰富的类和API&#xff0c;可以简化开发过…...

【股指期权投教】一手股指期权大概多少钱?

一手股指期权的权利金大概在几千人民币左右&#xff0c;如果是作为期权卖方还需要另外缴纳保证金的。国内的股指期权有三种&#xff0c;沪深300、上证50、中证1000股指期权&#xff0c;每点合约人民币100 元。 期权合约的价值计算可以通过此公式得出&#xff1a;权利金的支付或…...

mmap()函数和munmap()函数的例子

代码&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #include <string.h> #include <stdio.h> #include <unistd.h>#define FILELENGTH 80 int main(void) {int fd-1;char …...

计算神经网络中梯度的核心机制 - 反向传播(backpropagation)算法(1)

计算神经网络中梯度的核心机制 - 反向传播&#xff08;backpropagation&#xff09;算法&#xff08;1&#xff09; flyfish 链式法则在深度学习中的主要应用是在反向传播&#xff08;backpropagation&#xff09;算法中。 从简单的开始 &#xff0c;文本说的就是链式法则 R …...

VUE实现简易购物车

主要是对基础的指令的使用&#xff0c;直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">&l…...

混沌工程——从捣乱的视角看系统稳定性

概念 混沌工程是通过捣乱实验探究系统稳定性的实践过程&#xff0c;其作战武器是风险因子&#xff0c;即在健康的运行环境中引入风险变量来验证系统对风险的抵抗能力&#xff0c;它的作用是推动系统容错能力建设、验证监控告警及时性、提升研发问题排查能力。 混沌工程的工作…...

Windows宝塔面板部署ThinkPHP8.0创建Vue项目案例

安装ThinkPHP8.0 登录宝塔面板&#xff0c;创建一个站点。 输入composer代码&#xff0c;执行完成后自动创建TP目录 composer create-project topthink/think tp 网站目录设置为tp&#xff0c;运行目录设置为public 设置PHP版本为8.0以上&#xff0c;不然会出现下面的报错代…...

5G频段简介

5G频段 5G网络一共有29个频段&#xff0c;主要被分为两个频谱范围&#xff0c;其中6GHz以下的频段共有26个&#xff08;统称为Sub6GHz&#xff09;&#xff0c;毫米波频段有3个。目前国内主要使用的是Sub6GHz&#xff0c;包括n1/n3/n28/n41/n77/n78/n79共7个频段。具体介绍如下…...

【python学习】bytearray 数组

在Python中&#xff0c;bytearray 是一个可变序列&#xff0c;用于表示一个字节数组。与不可变的 bytes 类型相比&#xff0c;bytearray 允许你修改其内容。你可以通过索引来访问和修改 bytearray 中的元素&#xff0c;也可以添加或删除元素。 使用 bytearray 的一些示例&…...

Labview_Occurrencel(事件发生)

PS&#xff1a;这里遇到 一个很Low的事情&#xff1a; 在停止第二个while循环的时候出现了停止不了的情况。因为等待事件发生设置的超时时间为:-1。所以等事件发生后出现了条件接线端已经执行的情况&#xff0c;所以当下次事件发生时未能及时停止。初版的停止设置如下图&#x…...

天气网站爬虫及可视化

摘要&#xff1a;随着互联网的快速发展&#xff0c;人们对天气信息的需求也越来越高。本论文基于Python语言&#xff0c;设计并实现了一个天气网站爬虫及可视化系统。该系统通过网络爬虫技术从多个天气网站上获取实时的天气数据&#xff0c;并将数据进行清洗和存储。同时&#…...

逻辑图框架图等结构图类图的高效制作方式不妨进来看看

**逻辑图框架图等结构图类图的高效制作方式不妨进来看看** 基于我们每天都在处理大量的数据和信息。为了更清晰地理解和传达这些信息&#xff0c;结构图、逻辑图和框架图等可视化工具变得越来越重要。然而&#xff0c;如何高效地制作这些图表并确保其准确性和易读性呢&#xf…...

基于java+springboot+vue实现的图书商城管理系统(文末源码+Lw)283

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…...

ELK优化之Elasticsearch

目录 1.ELK优化 2.优化 ES 索引设置 2.1 优化 fsync 2.2 优化 refresh 2.3 优化 merge 2.4 优化设置 2.5 打开索引 3.优化线程池配置 3.1 优化的方案 4.锁定内存&#xff0c;不让 JVM 使用 Swap 5.减少分片数、副本数 6.ES优化总结 1.ELK优化 ELK优化可以围绕着 li…...

大力出奇迹:大语言模型的崛起与挑战

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;特别是在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;大语言模型&#xff08;LLM&#xff09;的出现与应用&#xff0c;彻底改变了我们与机器互动的方式。本文将探讨ChatGPT等大语言模型的定义、…...

MySQL—创建查看删除备份恢复数据库

创建数据库 创建数据库 LLF_DB01CREATE DATABASE LLF_DB01删除数据库DROP DATABASE LLF_DB01创建一个使用utf8字符集的数据库并带校对规则的数据库CREATE DATABASE hsp_db03 CHARACTER SET utf8 COLLATE utf8_bin 查看、删除数据库 显示所有的数据库SHOW DATABASES显示数据库…...

速盾:cdn加速效果

CDN&#xff08;Content Delivery Network&#xff09;即内容分发网络&#xff0c;是一种通过在全球多个节点服务器上缓存网站的静态资源&#xff0c;并将用户请求导向离用户最近的服务器节点&#xff0c;从而提供更快速的访问体验的技术。 在传统的网络架构中&#xff0c;用户…...

相差8000元!全新凯美瑞买燃油版,还是混动版?

前不久,广汽丰田全新凯美瑞正式上市,新车共有9款配置,分为2.0L燃油版和2.0L混动版,售价从17.18-20.68万元。全新凯美瑞在搭载2.0L混合动力总成后,售价方面有了大幅度的下调,17.98万元起步,顶配20.68万元,确实很有诚意。本期话题,我们就以凯美瑞入门的精英版两款车型来…...

0基础认识C语言(理论+实操 2)

小伙伴们大家好&#xff0c;今天也要撸起袖子加油干&#xff01;万事开头难&#xff0c;越学到后面越轻松~ 话不多说&#xff0c;开始正题~ 前提回顾&#xff1a; 接上次博客&#xff0c;我们学到了转义字符&#xff0c;最后留下两个转义字符不知道大家有没有动手尝试了一遍&a…...

tomcat学习--部署java项目

主流开发项目&#xff0c;springboot框架下&#xff0c;jar部署java传统的tomcat发布war包 一 什么是tomcat&#xff1f; 是一个用于运行java程序的软件&#xff0c;发布的时候&#xff1a;开发将源码使用maven打包&#xff0c;生产war包 二 安装tomcat tomcat是java写的&a…...

Day27

Day27 反射案例 案例一&#xff1a;万能数组扩容 注意&#xff1a;copyOf、toString public class Test01 {/*** 知识点&#xff1a;反射案例 之 万能数组扩容* * 注意&#xff1a;copyOf、toString*/public static void main(String[] args) {String[] ss {"aaa"…...

Java日志体系概述

一. 日志体系分类 1.1 功能分类 1.2 jar包分类 二. 以log4j2为例 2.1 slf4j-api的初始化动态绑定过程 一. 日志体系分类 1.1 功能分类 门面类 slf4j&#xff1a;Simple Logging Facade for Java&#xff0c;为java提供的简单日志Facade具体实现类 logbacklog4j1log4j2jul&…...

48、spfa求最短路

spfa求最短路 题目描述 给定一个n个点m条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你求出1号点到n号点的最短距离&#xff0c;如果无法从1号点走到n号点&#xff0c;则输出impossible。 数据保证不存在负权回路。 输入格式 第一行包…...