Microservice with FastAPI

What are microservices ? 什么是微服务? 微服务可以有多种不同的定义方式, 具体取决于希望强调微服务架构的哪个方面, 不同作者会给出略有不同但相关的定义 Sam Newman, 微服务领域最有影响力的作者之一, 给出了一个极简的定义: “Microservices are small, autonomous services that work together.” 这个定义强调了这样一个事实: 微服务是彼此独立运行的应用程序, 但它们可以协作完成任务. 该定义还强调微服务是 “small (小的)”, 这里的 small 并不是指微服务代码量的大小, 而是指微服务具有狭窄且定义清晰的职责范围, 符合单一职责原则(Single Responsibility Principle) —— 即“只做一件事,并把它做好”. James Lewis 和 Martin Fowler 撰写的一篇开创性文章提供了一个更详细的定义, 他们将微服务定义为一种架构风格(architectural style) “an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API” ...

August 15, 2025 · 6 min · 1167 words · Starslayerx

Python Generics

本篇文件介绍 Python 中的 泛型(Generics) Intro 在没有泛型的情况下, 会遇上以下几个问题: 难以表达意图 假设你编写了一个函数, 它接受一个列表, 并返回列表中的第一个元素. 在不使用类型提示的情况下, 这个函数可以处理任何类型的列表, 但我们无法在函数签名中表达"返回的元素的类型与列表中的元素类型相同"这个意图 def get_first_element(items): return items[0] 丧失类型信息 如果使用类型提示, 可能会像下面这样写, 但这样会丢失类型信息. list[Any] 表示可以接收任何类型的列表, 但 -> Any 意味着不知道返回的元素类型是什么, 这使得 mypy 等静态类型检测工具无法追踪类型, 降低了代码的可读性和安全性 from typing import Any def get_first_element(items: list[Any]) -> Any: return items[0] # 调用时, 类型检查工具无法得知 first_str 的类型 first_str = get_first_element(["hello", "world"]) 代码重复 如果为每种可能的类型都编写一个单独的函数, 则会导致代码重复 def get_first_int(items: list[int]) -> int: return items[0] def get_first_str(items: list[str]) -> str: return items[0] 通过引入 类型变量 (TypeVar) 来解决问题, 类型变量就像一个占位符, 代表在未来某时刻会被具体指定的类型 from typing import TypeVar T = TypeVar("T") def get_first_element(items: list[T]) -> T: return items[0] # 现在, 类型检查工具可以正确推断出类型 first_str: str = get_first_element(["hello", "world"]) first_int: int = get_first_element([1, 2, 3]) T = TypeVar('T') 定义了一个名为 T 的类型变量, 这里 T 只是一个约定俗成的名字, 也可以使用其他字母 items: list[T] 表示 items 是一个列表, 其内部元素类型是 T -> T: 返回类型也是 T 当使用 ["hello", "world"] 调用函数时, 静态类型检查器会推断出 T 是 str, 返回类型为 str 当使用 [1, 2, 3] 调用函数时, T 被推断为 int 注意: 这个函数假设列表非空, 如果传入空列表会抛出 IndexError ...

August 14, 2025 · 3 min · 553 words · Starslayerx

Python Strings

这篇文章总结一下 Python 中字符串的类型 Unicode String 字符串 u 在 Python3 中是多余的, 因为所有的普通字符串默认都是 Unicode, 但在 Python2 中, u 用来显示的表示 Unicode 字符串, 现在保留这个是为了向后兼容 Fromatted String 格式化字符串 f 前缀用于创建格式化字符串, 这是最常见的字符串格式方法, 运行在字符串中嵌入表达式, 在求值时转换为普通的 str name = "World" greeting = f"Hello, {name}!" # 结果: "Hello, World!" Raw String 原始字符串 r 前缀用于创建原始字符串, 会忽略反斜杠 \ 的转义功能, 在编写文件路径或正则表达式的时候非常有用, 可以避免大量的反斜杠转义 path = r"C:\Users\Documents" # 单个反斜杠 ' regex = r"\bword\b" # \b 不会被转义 Bytes String 字节串 b 前缀用于创建字节串字面量, 表示一个不可变的字节序列, 而不是 Unicode 文本, 字节串主要用于二进制数据, 例如图像文件、网络数据或压缩文件等 binary_data = b"Hello" # 存储的是 ASCII 编码的字节 Template String 模板字符串 t 前缀用于创建模板字符串, 这是 Python 3.14 引入的新功能, 由 PEP 750 通过. ...

August 13, 2025 · 1 min · 151 words · Starslayerx

FastAPI Response Model

本篇文章介绍 FastAPI 的返回类型 response model 可以在返回函数的类型注解中声明该接口的响应数据类型 类型注解的用法和输入数据参数一样, 可以使用: Pydantic 模型 list 列表 dict 字典 scalar 标量值 (int, bool …) @app.post("/items/") async def create_item(item: Item) -> Item: ... @app.get("/items/") async def read_items() -> list[Item]: ... FastAPI 会使用返回类型完成一下事情: 验证返回类型 如果返回的数据无效, 说明业务代码有问题, FastAPI 会返回服务器错误, 而不是把数据发给客户端 在 OpenAPI 中为响应添加 JSON Schema 用于自动生成接口文档, 自动生成客户端代码 最重要的是 它会限制并过滤出数据, 只保留返回类型中定义的字段 response_model Parameter 有时候可能需要返回的数据和类型注解不完全一致, 例如: 可能想返回字典或数据库对象, 但声明的响应类型为 Pydantic 模型 这样 Pydantic 会做数据文档、验证等工作, 即使返回的是字典或 ORM 对象 如果直接用返回类型注解, 编辑器会提示类型不匹配的错误 这种情况下, 可以用路径装饰器的 response_model 参数来声明响应类型, 而不是用返回类型注解 class Item(BaseModel): name: str description: str | None = None price: float tax: float | None = None tags: 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}, ] 注意: ...

August 12, 2025 · 5 min · 918 words · Starslayerx

Fastapi Cookie and Header Parameters

这篇文章介绍 Fastapi 的 Cookie 和 Header 参数 Cookie Parameters 通过定义 Query 和 Path 参数一样定义 Cookie 参数 from typing Annotated from fastapi import Cookie, FastAPI app = FastAPI() @app.get("/items/") async def read_items(ads_id: Annotated[str | None, Cookie()] = None): return {"ads_id": ads_id} Cookie Parameters Models 如果有一组相关的 cookies, 可以使用 Pydantic model 来声明. 这样可以在多个部分复用这个模型, 同时还能一次性为所有参数声明验证规则和元数据. 下面使用 Pydantic 模型定义 Cookies, 然后将参数声明为 Cookie from typing import Annotated from fastapi import FastAPI, Cookie from pydantic import BaseModel app = FastAPI() class Cookie(BaseModel): session_id: str fatebook_tracker: str | None = None googall_tracker: str | None = None @app.get("/items/") async def read_items(cookies: Annotated[Cookies, Cookie()]): return cookies Forbid Extra Cookies 禁止额外的Cookie 在某些场景下(虽然并不常见), 可能希望限制 API 只能接收特定的 Cookie. 这样, API 就可以"自己"管理 Cookie 同意策略了. from typing import Annotated from fastapi import FastAPI, Cookie from pydantic import BaseModel app = FastAPI() class Cookies(BaseModel): model_config = {"extra": "forbid"} # forbid extra cookies session_id: str fatebook_tracker: str | None = None googall_tracker: str | None = None @app.get("/items/") async def read_items(cookies: Annotated[Cookies, Cookie()]): return cookies 这样, 如果客户端发送额外的 cookies, 则会收到一个错误响应. 例如, 客户端发送了 santa_tracker 这个额外 Cookie ...

August 11, 2025 · 3 min · 499 words · Starslayerx

Python Function Parameters

今天是周日, 简单写点吧, 简单总结一下 Python 中函数参数 Python Function Parameters Python 函数参数机制非常灵活丰富, 理解各种参数类型及其用法对于写出优雅、易维护的代码非常重要. 本文将介绍 Python 中函数参数的种类与用法, 并详细讲解 Python 3.8 引入的参数分隔符 / 和 *, 帮助你更好地设计函数接口. 1. Postional Arguments 位置参数 函数定义中最常见的参数, 调用时按顺序传入值 def greet(name, age): print(f"Hello, {name}. You are {age} years old.") greet("Alice", 30) # Hello, Alice. You are 30 years old. 2. Keyword Arguments 关键字参数 调用时以 key=value 形式传入, 顺序可变 greet(age=30, name="Alice") 3. Default Arguments 默认参数 定义函数时给参数赋默认值, 调用时可省略 def greet(name, age=20): print(f"Hello, {name}. You are {age} years old.") greet("Bob") # 使用默认年龄20 greet("Bob", 25) # 指定年龄 注意: 使用默认参数尽量不要使用可变类型(mutable), 例如列表, 因为默认参数是存储在函数中的, 而非函数实例中, 多次调用会改变默认值的内容. def greet(names: list[str] = ["Alice", "Bob"]): ... 若希望使用默认值, 建议使用下面这种方法 ...

August 10, 2025 · 2 min · 278 words · Starslayerx

FastAPI Body Advanced Uses

本篇文章介绍 FastAPI Request Body 的进阶用法 Body - Multiple Parameters 首先, 可以将Path, Query 和 request body 参数声明自由的写在一起 对于 request body 参数可以是可选的, 并且可设置为默认的 None from typing import Annotated from fastapi import FastAPI, Path from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str description: str | None = None price: float tax: float | None = None @app.put("/items/{item_id}") async def update_item( item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)], # Path q: str | None = None, # Query item: Item | None = None, # body ): results = {"item_id": item_id} if q: results.update({"q": q}) if item: results.update({"item": item}) return results Multiple body parameters 多参数请求体 在上面例子中, FastAPI 期望一个包含 Item 属性的 JSON body, 例如 { "name": "Foo", "description": "The pretender", "price": 42.0, "tax": 3.2 } 但也可以声明多个body parameters, 例如 item 和 user ...

August 9, 2025 · 5 min · 911 words · Starslayerx

Git Whitelist

有时你开启了一个新的项目, 运行了 cargo init、uv init 和 go mod init 这些命令创建了工作所需要的必要文件, 同时也在 .gitignore 文件中添加了以下内容 target __pycache__ bin 一切都很顺利, 你继续开发新功能, 等到时机成熟时就将项目发布到了 Git 托管平台上 人们开始对你的项目感兴趣, 甚至有人决定为你实现一个新功能, 这简直是免费劳动力! 当你查看代码, 发现了一个格格不入的文件 .DS_Store, 你问那个人这是什么, 他说他根本不知道 然后你只是将该文件从分支里面删除, 并把文件名加入了仓库的 .gitignore target __pycache__ bin .DS_Store 现在代码合并到了 main, 仓库里只包含有用的内容 接着, 另一人使用基于 Web 技术的 IDE 提交了另一个合并请求, 一看发现有一个完全无关的目录也被提交了, 于是 .gitignore 里又增加了一条内容 target __pycache__ bin .DS_Store .vscode 接下来, 有人使用 IntelliJ IDEA 提交了五百个 XML 文件和 .idea 目录, 这时又不得不将其加入 .gitignore target __pycache__ bin .DS_Store .vscode .idea 多年后, .gitignore 已经有了上百行, 但是仍然时不时有各种奇怪的文件, 例如 testscripts、foo、a、qux、data.tar.gz、start.sh、cat …… ...

August 8, 2025 · 1 min · 127 words · Starslayerx

FastAPI Parameters and Validations

这篇文章介绍 FastAPI 中的参数验证功能 Query Parameters and String Validations FastAPI 允许为参数声明额外的信息和验证规则 from fastapi import FastAPI app = FastAPI() @app.get("/items/") async def read_items(q: str | None = None): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results q 是类型为 str | None 的查询参数, 这意味着它可以是字符串, 也可以是 None. 其默认值是 None, 因此 FastAPI 会识别它为“可选参数” FastAPI 通过 = None 的默认值知道该参数是非必填的 使用 str | None 还能帮助编辑器提供更好的类型提示和错误检测 Additional validation 额外验证 即使 q 是可选的, 但仍然可以设置条件: 如果提供了 q, 则长度不能超过50个字符 使用 Query 和 Annotated 来实现 from typing import Annotated from fastapi import FastAPI, Query app = FastAPI() @app.get("/items/") async def read_items(q: Annotated[str | None, Query(max_length=50)] = None): results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]} if q: results.update({"q": q}) return results 使用 Annotated 包装后, 就可以传递额外的元数据(Query(max_length=5)), 用于校验或者文档 ...

August 7, 2025 · 4 min · 779 words · Starslayerx

FastAPI Parameters

FastAPI 是一个现代、快速(高性能)的 Python Web 框架, 它自动处理参数的解析、验证和文档生成 本文将介绍 FastAPI 中三类最常用的参数: 路径参数 (Path Parameters)、查询参数 (Query Parameters) 和 请求体(Request Body) 的用法与原理 1. Path Parameters 路径参数 路径参数是 URL 路径中的动态部分, 使用 {} 包裹表示 from fastapi import FastAPI app = FastAPI() @app.get("/items/{item_id}") async def read_item(item_id: str): return {"item_id": item_id} 访问 /items/foo 返回: {"item_id": "foo"} Data conversion & validation 类型声明与自动转换 可以为路径参数声明类型, FastAPI 会自动解析并验证: @app.get("/items/{item_id}") async def read_item(item_id: int): return {"item_id": item_id} 访问 /items/3, item_id 会被转换为 int 类型 Routing orders 路由匹配顺序 路径匹配按声明顺序执行, 例如 @app.get("/users/me") async def read_user_me(): return {"user_id": "current_user"} @app.get("/users/{user_id}") async def read_user(user_id: str): return {"user_id": user_id} 必须先声明 /users/me, 否则会被 /users/{user_id} 捕获 ...

August 6, 2025 · 3 min · 525 words · Starslayerx