前端转agent-第一周-python-03 FastAPI与Pydantic实战 TS/JS视角

作者:袖梨 2026-07-21

前端转Python后端:用Java/Spring Boot的思维快速上手FastAPI

作为一名主修前端但也写过Java增删改查的开发者,曾经熟悉 @RestController@GetMapping@Valid 这些注解。现在决定用 Python 的 FastAPI 写后端,惊喜地发现:原来写接口可以这么轻量,却又如此规范。

前端转agent-第一周【python】-03 FastAPI与Pydantic实战(TS/JS视角)

本文跳过 Python 语法基础,直接用 Java/Spring Boot 的常用概念来类比 FastAPI 的核心功能。读完你会感觉,FastAPI 就像是把 Spring Boot 的注解换成了 Python 类型注解和装饰器,并且去掉所有 XML、自动配置和慢启动。

1. 概念映射:Spring Boot → FastAPI

Java / Spring BootPython / FastAPI
@RestController@app.get("/") 等装饰器 + 普通函数
@GetMapping("/path/{id}")@app.get("/path/{id}") + 函数参数类型注解
@PathVariable Long id函数参数 id: int
@RequestParam(defaultValue=...)函数参数 page: int = 1
@RequestBody + DTOPydantic BaseModel
@Valid + JSR 303 注解Pydantic 类型约束自动校验
@Autowired / 构造函数注入Depends() 函数
@Transactional + JPASQLAlchemy 或直接数据库驱动(可自行集成)
@Asyncasync def 异步函数
SpringDoc / Swagger自动生成 /docs/redoc
Tomcat / NettyUvicorn (ASGI 服务器)

可以把 FastAPI 看作 “原生 TypeScript 接口 + 自动校验 + 自动文档”的 Spring Boot 精简版。

2. 启动第一个接口:无需项目生成器

Spring Boot 需要 Initializr、pom.xml 和各种 starter。
FastAPI 只需要两个包:

mkdir demo && cd demopython -m venv venvsource venv/bin/activate # Windows: venvScriptsactivatepip install fastapi uvicorn

创建一个 main.py

from fastapi import FastAPIapp = FastAPI()@app.get("/")async def hello():return {"message": "Hello FastAPI"}

对比java的写法:

// Spring Boot@RestControllerpublic class HelloController {@GetMapping("/")public Map<String, String> hello() {return Map.of("message", "Hello Spring Boot");}}

启动服务:

uvicorn main:app --reload

  • mainmain.py
  • app 指 FastAPI 实例
  • --reload 热重载,类似 Spring DevTools

3. 路径参数:用函数参数代替 @PathVariable

FastAPI 把路径变量作为函数参数,并加上类型注解,框架会自动解析和校验。

// Spring Boot@GetMapping("/users/{id}")public User getUser(@PathVariable Long id) {return new User(id, "User-" + id);}

# FastAPI@app.get("/users/{user_id}")async def get_user(user_id: int):return {"user_id": user_id, "name": f"User-{user_id}"}

请求 /users/abc 会收到 422 Unprocessable Entity,提示类型错误,完全不需要你写判断。

4. 查询参数:抛弃 @RequestParam

查询参数通过默认值直接声明,不需要任何注解。

// Spring Boot@GetMapping("/search")public Map<String, Object> search(@RequestParam(defaultValue = "") String q,@RequestParam(defaultValue = "1") int page,@RequestParam(defaultValue = "10") int size) {return Map.of("q", q, "page", page, "size", size);}

# FastAPI@app.get("/search")async def search(q: str = "", page: int = 1, size: int = 10):return {"q": q, "page": page, "size": size}

同样,传入不合法的类型(如 page=abc)会直接返回 422。

5. 请求体 + 校验:Pydantic = DTO + @Valid 一体化

在 Spring Boot 中,你需要创建 DTO 并加注解:

public class CreateUserRequest {@NotBlankprivate String name;@Emailprivate String email;@Min(1)private int age = 18;// getters & setters...}@PostMapping("/users")public ResponseEntity<?> createUser(@Valid @RequestBody CreateUserRequest request) {// 校验通过后的逻辑return ResponseEntity.ok(...);}

在 FastAPI 里,定义一个 Pydantic 模型,类型本身就是校验规则:

from pydantic import BaseModel, EmailStrclass CreateUserRequest(BaseModel):name: stremail: EmailStr# 需要 pydantic[email] 扩展age: int = 18@app.post("/users")async def create_user(user: CreateUserRequest):# user 已经是经过完整校验的实例return {"id": 1, **user.model_dump()}

Pydantic 支持字符串长度、正则、范围、自定义校验器等,相当于 JSR 303 的 Pythonic 版本。

6. 动手写一个完整的 CRUD(内存版)

作为会增删改查的开发者,下面这个示例应该瞬间理解:我们模拟用户数据的增删改查,不使用数据库,只用列表存储。

from fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelfrom typing import Listapp = FastAPI()# 相当于 Java 的 User DTOclass User(BaseModel):id: intname: stremail: strclass CreateUser(BaseModel):name: stremail: str# 模拟数据库fake_db: List[User] = []next_id = 1@app.get("/users", response_model=List[User])async def list_users():return fake_db@app.get("/users/{user_id}", response_model=User)async def get_user(user_id: int):for user in fake_db:if user.id == user_id:return userraise HTTPException(status_code=404, detail="User not found")@app.post("/users", response_model=User, status_code=201)async def create_user(body: CreateUser):global next_iduser = User(id=next_id, name=body.name, email=body.email)fake_db.append(user)next_id += 1return user@app.put("/users/{user_id}", response_model=User)async def update_user(user_id: int, body: CreateUser):for index, user in enumerate(fake_db):if user.id == user_id:updated = User(id=user_id, name=body.name, email=body.email)fake_db[index] = updatedreturn updatedraise HTTPException(status_code=404, detail="User not found")@app.delete("/users/{user_id}", status_code=204)async def delete_user(user_id: int):for index, user in enumerate(fake_db):if user.id == user_id:fake_db.pop(index)returnraise HTTPException(status_code=404, detail="User not found")

这与你在 Java 中写的 UserController + UserService + 一个 ArrayList 存储几乎没有区别,但代码更少,而且自动生成 OpenAPI 文档。

7. 依赖注入:用 Depends 替代 @Autowired

Spring 的依赖注入通过容器管理 Bean,FastAPI 则用 Depends 实现函数级的依赖。

一个典型的 Service 注入场景:

// Spring@Servicepublic class GreetingService {public String greet(String name) { return "Hello " + name; }}@RestControllerpublic class GreetController {private final GreetingService service;public GreetController(GreetingService service) {this.service = service;}@GetMapping("/greet/{name}")public String greet(@PathVariable String name) {return service.greet(name);}}

FastAPI 版本:

from fastapi import Depends# 模拟服务def get_greeting_service():return {"greet": lambda name: f"Hello {name}"}# 实际返回类实例更好@app.get("/greet/{name}")async def greet(name: str, service: dict = Depends(get_greeting_service)):return service["greet"](name)

实际项目中,Depends 常用于注入数据库会话、当前用户、配置等,与 Spring 的拦截器和 AOP 有异曲同工之妙,但实现更轻量。

8. 异步:从 @Asyncasync/await

Spring Boot 使用 @Async + CompletableFuture,FastAPI 直接原生支持异步。

import asyncio@app.get("/async-greet")async def async_greet():await asyncio.sleep(1) # 模拟耗时I/Oreturn {"message": "greetings after 1 sec"}

对于 CPU 密集型操作,直接使用普通 def 函数,FastAPI 会自动将其放入线程池执行,避免阻塞事件循环。

9. 自动文档:SpringDoc 可以扔掉了

启动后访问:

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc

所有接口、参数、请求体、响应模型全部自动生成,并且可以直接在网页上测试。再也不用写 SpringDoc 注解或担心文档与代码不同步。

10. 推荐项目结构(类比 Spring Boot 分层)

myapi/├── main.py # 入口,相当于 @SpringBootApplication├── routers/# 路由层,相当于 @RestController│ └── users.py├── models/ # Pydantic 模型,相当于 DTO│ └── user.py├── services/ # 业务层,相当于 @Service│ └── user_service.py└── dependencies.py # 公共依赖,相当于 @Bean 或配置

路由文件示例 routers/users.py

from fastapi import APIRouter, Dependsfrom models.user import User, CreateUserfrom services.user_service import UserServicerouter = APIRouter(prefix="/users", tags=["users"])@router.get("/", response_model=list[User])async def list_users(service: UserService = Depends()):return service.get_all()

main.py 中挂载路由:

from fastapi import FastAPIfrom routers import usersapp = FastAPI()app.include_router(users.router)

这种结构对你来说非常熟悉:Controller 层变成 routers,Service 层还是 services,DTO 变成 models(Pydantic),而 main.py 就是启动类。

11. 总结:Java 后端到 FastAPI 的三个心智转变

  1. 注解变类型:@PathVariable@RequestParam@Valid 全部消失,取而代之的是 Python 类型注解和 Pydantic 模型。类型即校验,声明即文档。
  2. 容器变函数:没有 Spring 容器和依赖注入框架,Depends 只是一个普通的依赖函数。所有组件都是简单的 Python 对象,你可以更自由地组合它们。
  3. 配置变代码:不需要 application.yml 来配置端口、扫描包,代码就是配置。极端透明,启动迅速。

作为前端,我会喜欢 FastAPI 的简洁和现代感;作为写过 Java 增删改查的开发者,会惊讶于它用如此少的代码就完成了原本需要多层注解和配置的任务。

相关文章

精彩推荐