首页 > 教程攻略 > ai教程 >前端转agent-第一周【python】-03 FastAPI与Pydantic实战(TS/JS视角)

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

来源:互联网 时间:2026-07-21 07:18:07

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

如果你也是前端出身,但又写过Ja va增删改查,一定对 @RestController@GetMapping@Valid 这些注解再熟悉不过了。现在想用 Python 的 FastAPI 写后端,等你真正上手会发现:原来写接口可以这么轻量,却还能保持如此规范的开发体验。

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

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

1. 概念映射:Spring Boot → FastAPI

Ja va / 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 demo
python -m venv venv
source venv/bin/activate  # Windows: venvScriptsactivate
pip install fastapi uvicorn

创建一个 main.py

from fastapi import FastAPI
app = FastAPI()

@app.get("/")
async def hello():
    return {"message": "Hello FastAPI"}

对比一下Ja va的写法:

// Spring Boot
@RestController
public class HelloController {
    @GetMapping("/")
    public Map 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 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 {
    @NotBlank
    private String name;
    @Email
    private 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, EmailStr

class CreateUserRequest(BaseModel):
    name: str
    email: 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, HTTPException
from pydantic import BaseModel
from typing import List

app = FastAPI()

# 相当于 Ja va 的 User DTO
class User(BaseModel):
    id: int
    name: str
    email: str

class CreateUser(BaseModel):
    name: str
    email: 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 user
    raise 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_id
    user = User(id=next_id, name=body.name, email=body.email)
    fake_db.append(user)
    next_id += 1
    return 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] = updated
            return updated
    raise 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)
            return
    raise HTTPException(status_code=404, detail="User not found")

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

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

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

一个典型的 Service 注入场景:

// Spring
@Service
public class GreetingService {
    public String greet(String name) { return "Hello " + name; }
}

@RestController
public 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/O
    return {"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, Depends
from models.user import User, CreateUser
from services.user_service import UserService

router = 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 FastAPI
from routers import users

app = FastAPI()
app.include_router(users.router)

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

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

  1. 注解变类型

    @PathVariable@RequestParam@Valid 全部消失,取而代之的是 Python 类型注解和 Pydantic 模型。类型即校验,声明即文档。
  2. 容器变函数

    :没有 Spring 容器和依赖注入框架,Depends 只是一个普通的依赖函数。所有组件都是简单的 Python 对象,你可以更自由地组合它们。
  3. 配置变代码

    :不需要 application.yml 来配置端口、扫描包,代码就是配置。极端透明,启动迅速。

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