FastAPI 的初体验

如果你习惯 Spring Boot 里的 @SpringBootApplication@RestController@RequestBody 和统一的 Result<T>,第一次看 FastAPI 时,可以把 应用入口 想成 SpringApplication.run,把 路由 想成带 @RequestMapping 的 Controller,把 Pydantic 模型 想成 DTO / 校验 Bean。下面结合学习用的 fastapi-demo 项目,串一条最小但完整的路径。

应用入口:相当于「主类 + 扫包注册 Controller」

main.py 里创建 FastAPI() 实例,再把各模块的 APIRouter 挂上去,并挂载静态资源。对应 Spring 里通常是:一个主类 + @ComponentScan 自动发现 Controller;这里则是 显式 include_router,更像手动把各个 @ConfigurationRouterFunction 注册进主应用。

# 1:13:main.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

from controller.IndexController import router as index_router
from controller.AboutController import router as about_router
from controller.AuthController import router as auth_router

app = FastAPI()

app.include_router(index_router)
app.include_router(about_router)
app.include_router(auth_router)
app.mount("/static", StaticFiles(directory="static"), name="static")
  • app = FastAPI():应用本体,类似内嵌 Tomcat 的那个应用上下文。
  • include_router:把别的文件里定义的 router 合并进主应用,路径前缀可在 include_router(..., prefix="/api") 里统一加(当前未加前缀,所以各路由文件里写的路径就是最终路径)。
  • mount("/static", ...):类似 Spring MVC 里对 classpath:/static/ 的静态资源映射,这里指向本地 static 目录。

统一返回体:像 Result<T> / CommonResult

Java 里常封装 codemessagedata。这里用 entity/R.py 里的 Rsuccess / error 辅助函数表达同一意图(注意:文件名叫 R.pyfrom entity import R 时这里的 R 实际是 模块R.success 即模块级函数,不是 Java 那种静态方法写在类里,但用法上很像「Result.ok()」)。

# 1:13:entity/R.py
def success(message: str, data: object = None):
    return R(200, message, data)


def error(code: int, message: str, data: object = None):
    return R(code, message, data)


class R:
    def __init__(self, code: int, message: str, data: object = None):
        self.code = code
        self.message = message
        self.data = data

进阶提示:若要让 OpenAPI 文档里响应结构完全固定,往往会再包一层 Pydantic 的响应模型;当前直接返回自定义对象,能跑通,和 Spring 里直接返回 POJO 类似,细节可以后面再 refine。

最简接口:@GetMapping("/") 的对应物

IndexController 里用 APIRouter(),在函数上用 @router.get("/"),等价于在类上 @RequestMapping@GetMapping 到根路径。

# 1:9:controller/IndexController.py
from fastapi import APIRouter
from entity import R

router = APIRouter()


@router.get("/")
def index():
    return R.success("success")

Python 里没有「类上贴注解」的强制习惯,很多教程会把一簇路由放在一个模块里,导出同一个 router,这和 Spring 6 里 RouterFunction 或「一个 @RestController 类」都是同一种组织方式,只是风格不同。

登录 + DTO + JWT:像 LoginRequest + JwtUtil

  • LoginRequest:对应带校验的 DTO,用 Pydantic 的 BaseModel,字段可以写 Field(..., title="用户名"),会出现在自动生成的 Swagger 里,有点像 Bean Validation + OpenAPI 注解的合体。
  • @router.post("/login"):等价 @PostMapping("/login")
  • 函数参数 request: LoginRequest:FastAPI 会按 Content-Type: application/json 把 body 反序列化并校验,类似 @RequestBody LoginRequest request
# 1:28:controller/AuthController.py
import datetime
import jwt
from fastapi import APIRouter
from entity import R
from pydantic import BaseModel, Field

router = APIRouter(tags=["Auth"])


class LoginRequest(BaseModel):
    username: str = Field("", title="用户名")
    password: str = Field("", title="密码")

@router.post("/login")
def login(request: LoginRequest):
    if request.username == "admin" and request.password == "admin":
        # JWT
        token = jwt.encode(
            {
                "username": request.username,
                "exp": datetime.datetime.now() + datetime.timedelta(days=1)
            },
            "secret",
            algorithm="HS256"
        )
        return R.success("登陆成功", {"token": token})

    return R.success("用户名或密码错误")

router = APIRouter(tags=["Auth"]) 里的 tags 会在 Swagger UI 里分组,类似给 Controller 加 @Tag

学习项目里密码写死、密钥写 "secret" 都可以理解;对照 Spring Security,你会知道上线要换 密码编码、密钥外置、HTTPS 等,思路是通的。

流式响应:StreamingResponse 与异步生成器

下面这段演示 按片段输出,用 StreamingResponse + async def + yield。可以类比 Spring WebFlux 的 Flux<String> 流式返回,或 MVC 里 SseEmitter / 流式 ResponseBody,只是 Python 这边用 异步生成器 写起来很直接。

# 20:24:controller/AboutController.py
@router.get("/about", response_class=StreamingResponse)
async def stream_story() -> AsyncIterable[str]:
    for line in message:
        await asyncio.sleep(0.01)
        yield line

response_class=StreamingResponse 指明响应按「流」处理;yield line 在异步函数里构成 async generator,适合边生成边推给客户端(例如打字机效果、日志流、大模型 token 流)。

和 Spring Boot 对照的一张表

Spring Boot 里你熟悉的 FastAPI / 本项目里
@SpringBootApplication + 内嵌容器 app = FastAPI() + Uvicorn 等 ASGI 服务器运行
@RestController + @GetMapping / @PostMapping APIRouter + @router.get / @router.post
@RequestBody DTO 函数参数类型写成 Pydantic BaseModel
Result<T> / 统一包装 R.success / R.error + R 实例字段
ResourceHandler 静态资源 app.mount("/static", StaticFiles(...))
Springdoc / Swagger 访问 /docs(Swagger UI)

小结

对「只懂一点 Python」的 Spring 开发者来说,FastAPI 的门槛主要在 习惯缩进、模块与 router 的组织方式,以及 Pydantic 与类型标注;业务概念上 路由、DTO、统一返回、JWT、静态资源、流式响应 都能一一对应。把上面的关键代码跑通,再打开浏览器访问 /docs 试几次请求,就算是扎实的初体验了。

评论