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

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

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

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


1. 概念映射:Spring Boot → FastAPI

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

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


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

Spring Boot 需要 Initializr、pom.xml 和各种 starter。

FastAPI 只需要两个包:

bash 复制代码
mkdir demo && cd demo
python -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate
pip install fastapi uvicorn

创建一个 main.py

python 复制代码
from fastapi import FastAPI

app = FastAPI()

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

对比java的写法:

java 复制代码
// Spring Boot
@RestController
public class HelloController {
    @GetMapping("/")
    public Map<String, String> hello() {
        return Map.of("message", "Hello Spring Boot");
    }
}

启动服务:

bash 复制代码
uvicorn main:app --reload
  • mainmain.py
  • app 指 FastAPI 实例
  • --reload 热重载,类似 Spring DevTools

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

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

java 复制代码
// Spring Boot
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    return new User(id, "User-" + id);
}
python 复制代码
# 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

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

java 复制代码
// 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);
}
python 复制代码
# 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 并加注解:

java 复制代码
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 模型,类型本身就是校验规则:

python 复制代码
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(内存版)

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

python 复制代码
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List

app = FastAPI()

# 相当于 Java 的 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")

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


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

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

一个典型的 Service 注入场景:

java 复制代码
// 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 版本:

python 复制代码
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 直接原生支持异步。

python 复制代码
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 可以扔掉了

启动后访问:

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


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

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

路由文件示例 routers/users.py

python 复制代码
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 中挂载路由:

python 复制代码
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. 总结:Java 后端到 FastAPI 的三个心智转变

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

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

相关推荐
大圣编程1 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang1 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆2 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜3 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞4 小时前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农6 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器
2501_943782356 小时前
【共创季稿事节】猜数字游戏:二分法思维与交互式反馈
前端·游戏·microsoft·harmonyos·鸿蒙·鸿蒙系统
GV191rLvq7 小时前
基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
服务器·前端·asp.net
吠品7 小时前
LangChain 里 tool_call_id 为空?一次 MCP 工具集成的排查记录
前端