从 0 到 1 搭建 API:FastAPI 实战教程(含数据库、部署与安全)

FastAPI实战:从核心概念到项目部署,构建高性能Python API 在现代Web开发中,API作为应用间通信的核心桥梁,其性能、开发效率和可维护性至关重要。Python作为主流开发语言,其Web框架生态丰富,而FastAPI的出现,正是为了解决传统框架在类型安全、文档同步和并发性能上的痛点。其核心原理在于深度利用Python类型提示,通过Pydantic实现运行时数据验证与序列化,并结合异步编程模型,显著提升了I/O密集型任务的处理能力。这种设计带来了极高的技术价值:开发者能获得媲美Node.js和Go的性能,同时享受自动生成的、实时同步的交互式API文档,极大提升了开发体 阅读详情

目录

1. 引言

在当今的软件开发中,API(Application Programming Interface,应用程序编程接口)已经成为连接不同系统、服务和数据的关键桥梁。无论是构建 Web 应用、移动端 App,还是实现微服务架构,API 都扮演着不可或缺的角色。

然而,对于许多初学者来说,API 似乎是一个既熟悉又陌生的概念——每天都在使用别人提供的 API,却不知道如何从零开始设计和搭建一个属于自己的 API。本文将从最基础的概念出发,一步步带你完成一个 API 从设计、开发、测试到部署上线的完整流程。

2. 什么是 API

API 本质上是一组定义了软件组件之间如何交互的规则和协议。它允许不同的软件系统之间进行通信和数据交换,而无需了解对方内部的具体实现细节。

2.1 生活中的类比

可以把 API 想象成餐厅里的服务员:

  • 你(客户端)向服务员(API)点菜(发送请求)
  • 服务员将你的需求传达给厨房(服务器)
  • 厨房做好菜后,服务员再将菜品(响应数据)端到你面前

在这个过程中,你不需要知道厨房内部如何运作,只需要按照菜单(API 文档)点菜即可。

2.2 API 的核心组成

一个典型的 Web API 通常包含以下要素:

  • 端点(Endpoint):API 的访问地址,如 https://api.example.com/users
  • 方法(Method):HTTP 动词,如 GET、POST、PUT、DELETE
  • 参数(Parameters):请求中携带的数据,可以是路径参数、查询参数或请求体
  • 响应(Response):服务器返回的数据,通常为 JSON 或 XML 格式
  • 状态码(Status Code):表示请求结果的数字代码,如 200 表示成功、404 表示资源不存在

3. 搭建前的准备工作

在开始编写代码之前,我们需要先做好环境准备和需求分析。

3.1 技术选型

本文以 Python 的 FastAPI 框架为例进行演示,因为它具有以下优势:

  • 性能优异,基于 Starlette 和 Pydantic
  • 自动生成交互式 API 文档
  • 类型提示友好,代码可读性强
  • 支持异步编程

当然,你也可以选择其他技术栈,如 Node.js 的 Express、Java 的 Spring Boot 等,核心思路是相通的。

3.2 环境准备

首先,确保你的电脑上安装了 Python 3.8 及以上版本,然后创建虚拟环境并安装依赖:

# 创建项目目录
mkdir my-api
cd my-api

# 创建虚拟环境
python -m venv venv

# 激活虚拟环境(Windows)
venv\Scripts\activate

# 激活虚拟环境(macOS/Linux)
source venv/bin/activate

# 安装 FastAPI 和 Uvicorn
pip install fastapi uvicorn

3.3 需求分析

在动手写代码之前,先明确我们的 API 要解决什么问题。本文以一个简单的「待办事项管理」API 为例,需要实现以下功能:

  • 创建待办事项
  • 查询待办事项列表
  • 查询单个待办事项详情
  • 更新待办事项
  • 删除待办事项

4. 第一个 API 接口

现在,让我们从最简单的接口开始,感受一下 FastAPI 的开发体验。

4.1 创建主文件

在项目目录下创建 main.py 文件:

from fastapi import FastAPI

# 创建 FastAPI 实例
app = FastAPI(title="待办事项 API", version="1.0.0")


@app.get("/")
def read_root():
    return {
   
   "message": "欢迎使用待办事项 API"}

4.2 启动服务

在终端中运行以下命令启动开发服务器:

uvicorn main:app --reload

启动成功后,你会看到类似如下的输出:

INFO:     Uvicorn running on http://127.0.0.1:8000
INFO:     Application startup complete.

4.3 测试接口

打开浏览器访问 http://127.0.0.1:8000,你会看到返回的 JSON 数据:

{
   
   
  "message": "欢迎使用待办事项 API"
}

同时,FastAPI 还自动生成了交互式 API 文档,访问 http://127.0.0.1:8000/docs 即可查看和在线调试所有接口。

5. 设计数据模型

接下来,我们使用 Pydantic 定义待办事项的数据模型。Pydantic 是 FastAPI 的数据验证核心,可以自动完成请求数据的类型校验和序列化。

5.1 定义模型

在 main.py 中新增数据模型:

from typing import Optional
from pydantic import BaseModel
from datetime import datetime


class TodoItem(BaseModel):
    """待办事项数据模型"""
    id: Optional[int] = None
    title: str
    description: Optional[str] = None
    completed: bool = False
    created_at: datetime = datetime.now()

5.2 模型说明

  • id:待办事项的唯一标识,创建时自动生成
  • title:标题,必填字段
  • description:详细描述,可选字段
  • completed:是否已完成,默认为 False
  • created_at:创建时间,默认为当前时间

6. 实现增删改查

现在,我们来实现完整的 CRUD(Create、Read、Update、Delete)接口。

6.1 内存数据存储

为了简化演示,我们先使用内存列表存储数据。在实际项目中,你会使用数据库(如 PostgreSQL、MySQL)来持久化数据。

# 内存数据存储
todos = []
todo_id_counter = 1

6.2 创建待办事项

from fastapi import HTTPException


@app.post("/todos", status_code=201)
def create_todo(todo: TodoItem):
    """创建新的待办事项"""
    global todo_id_counter
    todo.id = todo_id_counter
    todo_id_counter += 1
    todos.append(todo)
    return todo

6.3 查询待办事项列表

@app.get("/todos")
def list_todos(completed: Optional[bool] = None):
    """获取待办事项列表,可按完成状态筛选"""
    if completed is None:
        return todos
    return [todo for todo in todos if todo.completed == completed]

6.4 查询单个待办事项

@app.get("/todos/{todo_id}")
def get_todo(todo_id: int):
    """根据 ID 获取单个待办事项"""
    for todo in todos:
        if todo.id == todo_id:
            return todo
    raise HTTPException(status_code=404, detail="待办事项不存在")

6.5 更新待办事项

@app.put("/todos/{todo_id}")
def update_todo(todo_id: int, todo_update: TodoItem):
    """更新指定 ID 的待办事项"""
    for index, todo in enumerate(todos):
        if todo.id == todo_id:
            # 保留原 ID 和创建时间
            todo_update.id = todo.id
            todo_update.created_at = todo.created_at
            todos[index] = todo_update
            return todo_update
    raise HTTPException(status_code=404, detail="待办事项不存在")

6.6 删除待办事项

@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo(todo_id: int):
    """删除指定 ID 的待办事项"""
    for index, todo in enumerate(todos):
        if todo.id == todo_id:
            todos.pop(index)
            return
    raise HTTPException(status_code=404, detail="待办事项不存在")

删除成功后返回 204 No Content,表示操作成功且无返回内容。如果待办事项不存在,则返回 404 错误。

6.7 全局异常处理

6.8 数据库集成

前面我们使用内存列表存储数据,服务重启后数据就会丢失。下面我们使用 SQLAlchemy 连接 SQLite 数据库,实现数据的持久化存储。

6.8.1 安装依赖
pip install sqlalchemy
6.8.2 配置数据库

在 main.py 中新增数据库配置和模型:

from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime
from sqlalchemy.orm import declarative_base, sessionmaker
from datetime import datetime

# 数据库配置(使用 SQLite,实际项目可替换为 PostgreSQL/MySQL)
DATABASE_URL = "sqlite:///./todos.db"
engine = create_engine(DATABASE_URL, connect_args={
   
   "check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()


class TodoDB(Base):
    """数据库中的待办事项表"""
    __tablename__ = "todos"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, nullable=False)
    description = Column(String, nullable=True)
    completed = Column(Boolean, default=False)
    created_at = Column(DateTime, default=datetime.now)


# 创建数据表
Base.metadata.create_all(bind=engine)
6.8.3 改造 CRUD 接口

将原来的内存操作替换为数据库操作:

from fastapi import Depends
from sqlalchemy.orm import Session


def get_db():
    """获取数据库会话"""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


@app.post("/todos", status_code=201)
def create_todo(todo: TodoItem, db: Session = Depends(get_db)):
    """创建新的待办事项(写入数据库)"""
    db_todo = TodoDB(
        title=todo.title,
        description=todo.description,
        completed=todo.completed,
    )
    db.add(db_todo)
    db.commit()
    db.refresh(db_todo)
    return db_todo


@app.get("/todos")
def list_todos(completed: Optional[bool] = None, db: Session = Depends(get_db)):
    """获取待办事项列表(从数据库读取)"""
    query = db.query(TodoDB)
    if completed is not None:
        query = query.filter(TodoDB.completed == completed)
    return query.all()


@app.get("/todos/{todo_id}")
def get_todo(todo_id: int, db: Session = Depends(get_db)):
    """根据 ID 获取单个待办事项"""
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="待办事项不存在")
    return todo


@app.put("/todos/{todo_id}")
def update_todo(todo_id: int, todo_update: TodoItem, db: Session = Depends(get_db)):
    """更新指定 ID 的待办事项"""
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="待办事项不存在")
    todo.title = todo_update.title
    todo.description = todo_update.description
    todo.completed = todo_update.completed
    db.commit()
    db.refresh(todo)
    return todo


@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo(todo_id: int, db: Session = Depends(get_db)):
    """删除指定 ID 的待办事项"""
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    if not todo:
        raise HTTPException(status_code=404, detail="待办事项不存在")
    db.delete(todo)
    db.commit()

改造完成后,重启服务,数据就会持久化保存到 todos.db 文件中,即使服务重启数据也不会丢失。

为了让 API 在出错时返回统一、友好的错误信息,我们添加一个全局异常处理器。这样无论是参数校验失败、资源不存在,还是服务器内部错误,客户端都能收到结构一致的 JSON 响应。

from fastapi import Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
    """统一处理 HTTP 异常,返回 JSON 格式的错误信息"""
    return JSONResponse(
        status_code=exc.status_code,
        content={
   
   "code": exc.status_code, "message": exc.detail, "data": None},
    )


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """统一处理请求参数校验失败,返回字段级别的错误详情"""
    errors = exc.errors()
    detail = []
    for error in errors:
        field = ".".join(str(loc) for loc in error["loc"] if loc != "body")
        detail.append({
   
   "field": field, "message": error["msg"]})
    return JSONResponse(
        status_code=422,
        content={
   
   "code": 422, "message": "请求参数校验失败", "data": detail},
    )


@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
    """兜底捕获未处理的异常,避免向客户端泄露堆栈信息"""
    return JSONResponse(
        status_code=500,
        content={
   
   "code": 500, "message": "服务器内部错误,请稍后重试", "data": None},
    )

添加后,所有接口的错误响应都会统一为 {"code": ..., "message": ..., "data": ...} 的结构,前端解析起来更加方便。

@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo(todo_id: int):
    """删除指定 ID 的待办事项"""
    for index, todo in enumerate(todos):
        if todo.id == todo_id:
            todos.pop(index)
            return
    raise HTTPException(status_code=404, detail="待办事项不存在")

7. 接口测试与调试

接口写完后,我们需要进行充分的测试,确保每个接口都能正常工作。除了 Swagger 和 curl,API 还支持多种调用方式,下面逐一介绍。

7.1 使用 Swagger 文档测试

FastAPI 自动生成的 /docs 页面提供了可视化的接口测试工具。你可以:

  1. 打开 http://127.0.0.1:8000/docs
  2. 点击任意接口展开详情
  3. 点击「Try it out」按钮
  4. 填写参数后点击「Execute」发送请求
  5. 查看响应结果和状态码

7.2 使用 curl 命令测试

在终端中也可以直接使用 curl 进行测试:

# 创建待办事项
curl -X POST "http://127.0.0.1:8000/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "完成 API 开发教程"}'

# 查询列表
curl "http://127.0.0.1:8000/todos"

# 查询单个
curl "http://127.0.0.1:8000/todos/1"

# 更新
curl -X PUT "http://127.0.0.1:8000/todos/1" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "已完成", "completed": true}'

# 删除
curl -X DELETE "http://127.0.0.1:8000/todos/1"

7.3 使用 Python requests 调用

在 Python 脚本或 Jupyter Notebook 中,可以使用 requests 库调用 API:

import requests

BASE_URL = "http://127.0.0.1:8000"

# 创建待办事项
resp = requests.post(
    f"{
     
     BASE_URL}/todos",
    json={
   
   "title": "学习 FastAPI", "description": "完成 API 开发教程"},
)
print(resp.status_code, resp.json())

# 查询列表
resp = requests.get(f"{
     
     BASE_URL}/todos")
print(resp.status_code, resp.json())

# 查询单个
resp = requests.get(f"{
     
     BASE_URL}/todos/1")
print(resp.status_code, resp.json())

# 更新
resp = requests.put(
    f"{
     
     BASE_URL}/todos/1",
    json={
   
   "title": "学习 FastAPI", "description": "已完成", "completed": True},
)
print(resp.status_code, resp.json())

# 删除
resp = requests.delete(f"{
     
     BASE_URL}/todos/1")
print(resp.status_code)

7.4 使用 JavaScript fetch 调用

在前端浏览器或 Node.js 环境中,可以使用 fetch 调用 API:

const BASE_URL = "http://127.0.0.1:8000";

// 创建待办事项
const createResp = await fetch(`${
     
     BASE_URL}/todos`, {
   
   
  method: "POST",
  headers: {
   
    "Content-Type": "application/json" },
  body: JSON.stringify({
   
   
    title: "学习 FastAPI",
    description: "完成 API 开发教程",
  }),
});
console.log(createResp.status, await createResp.json());

// 查询列表
const listResp = await fetch(`${
     
     BASE_URL}/todos`);
console.log(listResp.status, await listResp.json());

// 查询单个
const getResp = await fetch(`${
     
     BASE_URL}/todos/1`);
console.log(getResp.status, await getResp.json());

// 更新
const updateResp = await fetch(`${
     
     BASE_URL}/todos/1`, {
   
   
  method: "PUT",
  headers: {
   
    "Content-Type": "application/json" },
  body: JSON.stringify({
   
   
    title: "学习 FastAPI",
    description: "已完成",
    completed: true,
  }),
});
console.log(updateResp.status, await updateResp.json());

// 删除
const deleteResp = await fetch(`${
     
     BASE_URL}/todos/1`, {
   
    method: "DELETE" });
console.log(deleteResp.status);

7.5 使用 Postman / Apifox 图形化工具

Postman 和 Apifox 是流行的图形化 API 调试工具,适合团队协作和接口文档管理:

  1. 新建请求,选择方法(GET/POST/PUT/DELETE)
  2. 填写 URL,如 http://127.0.0.1:8000/todos
  3. 在 Body 选项卡选择 raw + JSON,填入请求体
  4. 点击 Send 发送请求,查看响应结果
  5. 可将接口保存到集合中,方便复用和分享

调用方式选择建议:日常调试用 Swagger 或 Postman;脚本自动化用 Python requests;前端联调用 fetch 或 axios;命令行快速验证用 curl。

接口写完后,我们需要进行充分的测试,确保每个接口都能正常工作。

7.1 使用 Swagger 文档测试

FastAPI 自动生成的 /docs 页面提供了可视化的接口测试工具。你可以:

  1. 打开 http://127.0.0.1:8000/docs
  2. 点击任意接口展开详情
  3. 点击「Try it out」按钮
  4. 填写参数后点击「Execute」发送请求
  5. 查看响应结果和状态码

7.2 使用 curl 命令测试

在终端中也可以直接使用 curl 进行测试:

# 创建待办事项
curl -X POST "http://127.0.0.1:8000/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "完成 API 开发教程"}'

# 查询列表
curl "http://127.0.0.1:8000/todos"

# 查询单个
curl "http://127.0.0.1:8000/todos/1"

# 更新
curl -X PUT "http://127.0.0.1:8000/todos/1" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "已完成", "completed": true}'

# 删除
curl -X DELETE "http://127.0.0.1:8000/todos/1"

7.3 常见问题排查

在开发和测试过程中,你可能会遇到一些常见问题。下面按错误类型给出排查思路和解决方案。

7.3.1 404 错误:资源不存在

现象:请求返回 404 Not Found。

可能原因与解决方案:

  • URL 路径写错:检查路径是否与路由定义完全一致,注意大小写和末尾斜杠
  • 资源 ID 不存在:确认查询的 ID 是否在数据库中存在,可先调用列表接口确认
  • 路由顺序问题:如果定义了 /todos/{todo_id} 和 /todos/me 这类路由,注意静态路径要放在动态路径之前
7.3.2 422 错误:参数校验失败

现象:请求返回 422 Unprocessable Entity,响应体中包含字段级别的错误详情。

可能原因与解决方案:

  • 缺少必填字段:如创建待办事项时未传 title,检查请求体是否完整
  • 字段类型错误:如 completed 传了字符串 "true" 而不是布尔值 true,检查 JSON 中的类型
  • 路径参数类型错误:如 /todos/abc 中 abc 无法转换为 int,确认路径参数类型
7.3.3 500 错误:服务器内部异常

现象:请求返回 500 Internal Server Error。

可能原因与解决方案:

  • 数据库连接失败:检查数据库服务是否启动、连接串是否正确
  • 代码逻辑异常:查看终端日志中的堆栈信息,定位具体报错行
  • 依赖缺失:确认所有依赖都已安装,可执行 pip list 检查
7.3.4 服务无法启动

现象:运行 uvicorn main:app --reload 时报错退出。

可能原因与解决方案:

  • 端口被占用:换一个端口启动,如 uvicorn main:app --port 8001
  • 导入错误:检查 main.py 中是否有语法错误或未安装的依赖
  • 虚拟环境未激活:确认当前终端使用的是项目虚拟环境
7.3.5 数据不持久化

现象:服务重启后,之前创建的数据丢失。

可能原因与解决方案:

  • 仍在使用内存存储:确认是否已按第 6.8 节改造为数据库存储
  • 数据库文件路径问题:检查 DATABASE_URL 中的路径是否正确,SQLite 文件是否生成
  • 未执行建表:确认 Base.metadata.create_all(bind=engine) 已执行

排查技巧:遇到问题时,先看终端日志中的堆栈信息,再结合响应体中的 code 和 message 字段定位问题,通常能快速找到根因。

7.4 接口性能对比

为了直观展示不同实现方式的性能差异,下面用一张对比图说明内存存储与数据库存储、同步与异步接口、有无缓存等场景下的响应耗时情况。

适用场景

缓存命中率

平均响应耗时

接口实现方式

内存存储
(无 I/O)

SQLite 存储
(本地磁盘 I/O)

PostgreSQL 存储
(网络 I/O)

SQLite + Redis 缓存
(缓存命中)

异步 PostgreSQL
(asyncpg)

0.1ms - 0.5ms

1ms - 5ms

5ms - 20ms

0.2ms - 1ms

3ms - 10ms

100%(无缓存)

0%(无缓存)

0%(无缓存)

90% - 99%

0%(无缓存)

缓存 / 临时数据

中小型应用 / 开发环境

生产环境 / 高并发

高频读取 / 低频更新

高并发写入 / 大数据量

从图中可以看出:

  • 内存存储:响应最快,但数据无法持久化,适合缓存或临时数据
  • SQLite 存储:本地磁盘 I/O,速度尚可,适合中小型应用和开发环境
  • PostgreSQL 存储:网络 I/O 开销较大,但支持并发、事务和水平扩展,适合生产环境
  • SQLite + Redis 缓存:缓存命中时响应接近内存速度,适合高频读取、低频更新的数据
  • 异步 PostgreSQL:通过异步驱动减少线程阻塞,在高并发下吞吐量显著提升

读多写少场景:优先考虑加缓存;写多读少场景:优先考虑异步驱动和连接池;高并发场景:两者结合效果最佳。

7.4.1 性能对比数据表

为了更直观地对比不同实现方式的性能差异,下面给出一个参考数据表(基于 1000 次请求的平均值):

实现方式 平均响应耗时 吞吐量(QPS) 缓存命中率 数据持久化 适用场景
内存存储 0.1ms - 0.5ms 5000+ 100%(无缓存) ❌ 缓存、临时数据
SQLite 存储 1ms - 5ms 800 - 1500 0%(无缓存) ✅ 中小型应用、开发环境
PostgreSQL 存储 5ms - 20ms 300 - 800 0%(无缓存) ✅ 生产环境、高并发
SQLite + Redis 缓存 0.2ms - 1ms 3000+ 90% - 99% ✅ 高频读取、低频更新
异步 PostgreSQL 3ms - 10ms 1000 - 3000 0%(无缓存) ✅ 高并发写入、大数据量

缓存命中率说明:缓存命中率越高,实际响应越接近内存速度。当缓存命中率低于 80% 时,建议检查缓存淘汰策略或调整过期时间,避免缓存穿透和雪崩。

7.5 实战测试:完整流程演练

下面通过一个完整的实战演练,把前面学到的接口串起来跑一遍,验证 CRUD 全流程的正确性。

7.5.1 准备测试数据

先创建两条待办事项作为测试数据:

# 创建第一条待办事项
curl -X POST "http://127.0.0.1:8000/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "完成 API 开发教程"}'

# 创建第二条待办事项
curl -X POST "http://127.0.0.1:8000/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "编写单元测试", "description": "为 CRUD 接口补充测试用例"}'

7.5.2 验证查询功能

# 查询列表,应返回 2 条记录
curl "http://127.0.0.1:8000/todos"

# 按完成状态筛选
curl "http://127.0.0.1:8000/todos?completed=false"

# 查询单个
curl "http://127.0.0.1:8000/todos/1"

7.5.3 验证更新功能

# 将第一条待办事项标记为已完成
curl -X PUT "http://127.0.0.1:8000/todos/1" \
  -H "Content-Type: application/json" \
  -d '{"title": "学习 FastAPI", "description": "完成 API 开发教程", "completed": true}'

7.5.4 验证删除功能

# 删除第二条待办事项
curl -X DELETE "http://127.0.0.1:8000/todos/2"

# 再次查询列表,应只剩 1 条记录
curl "http://127.0.0.1:8000/todos"

7.5.5 验证错误处理

# 查询不存在的 ID,应返回 404
curl "http://127.0.0.1:8000/todos/999"

# 缺少必填字段,应返回 422
curl -X POST "http://127.0.0.1:8000/todos" \
  -H "Content-Type: application/json" \
  -d '{"description": "缺少 title 字段"}'

7.5.6 使用 Python 脚本自动化测试

手动测试只能验证单个接口,下面用 Python 脚本把整个流程自动化跑一遍,并断言每个步骤的返回结果:

import requests

BASE_URL = "http://127.0.0.1:8000"


def test_create_todo():
    """测试创建待办事项"""
    resp = requests.post(
        f"{
     
     BASE_URL}/todos",
        json={
   
   "title": "学习 FastAPI", "description": "完成 API 开发教程"},
    )
    assert resp.status_code == 201, f"创建失败: {
     
     resp.status_code}"
    data = resp.json()
    assert data["title"] == "学习 FastAPI"
    print(f"✅ 创建成功: id={
     
     data['id']}")
    return data["id"]


def test_list_todos():
    """测试查询列表"""
    resp = requests.get(f"{
     
     BASE_URL}/todos")
    assert resp.status_code == 200
    assert isinstance(resp.json(), list)
    print(f"✅ 查询列表成功: 共 {
     
     len(resp.json())} 条")


def test_get_todo(todo_id: int):
    """测试查询单个"""
    resp = requests.get(f"{
     
     BASE_URL}/todos/{
     
     todo_id}")
    assert resp.status_code == 200
    assert resp.json()["id"] == todo_id
    print(f"✅ 查询单个成功: id={
     
     todo_id}")


def test_update_todo(todo_id: int):
    """测试更新"""
    resp = requests.put(
        f"{
     
     BASE_URL}/todos/{
     
     todo_id}",
        json={
   
   "title": "学习 FastAPI", "description": "已完成", "completed": True},
    )
    assert resp.status_code == 200
    assert resp.json()["completed"] is True
    print(f"✅ 更新成功: id={
     
     todo_id}")


def test_delete_todo(todo_id: int):
    """测试删除"""
    resp = requests.delete(f"{
     
     BASE_URL}/todos/{
     
     todo_id}")
    assert resp.status_code == 204
    print(f"✅ 删除成功: id={
     
     todo_id}")


def test_not_found():
    """测试 404 错误处理"""
    resp = requests.get(f"{
     
     BASE_URL}/todos/99999")
    assert resp.status_code == 404
    assert resp.json()["code"] == 404
    print("✅ 404 错误处理正确")


def test_validation_error():
    """测试 422 参数校验错误"""
    resp = requests.post(f"{
     
     BASE_URL}/todos", json={
   
   "description": "缺少 title"})
    assert resp.status_code == 422
    assert resp.json()["code"] == 422
    print("✅ 422 参数校验错误处理正确")


if __name__ == "__main__":
    print("开始自动化测试...\n")
    todo_id = test_create_todo()
    test_list_todos()
    test_get_todo(todo_id)
    test_update_todo(todo_id)
    test_delete_todo(todo_id)
    test_not_found()
    test_validation_error()
    print("\n🎉 全部测试通过!")

运行脚本:

python test_api.py

预期输出:

开始自动化测试...

✅ 创建成功: id=1
✅ 查询列表成功: 共 1 条
✅ 查询单个成功: id=1
✅ 更新成功: id=1
✅ 删除成功: id=1
✅ 404 错误处理正确
✅ 422 参数校验错误处理正确

🎉 全部测试通过!

测试要点:自动化测试脚本应覆盖正常流程(增删改查)和异常流程(404、422),并断言状态码和关键字段。实际项目中可结合 pytest 编写更完整的测试套件,并接入 CI/CD 流水线。

7.5.7 使用 pytest 编写自动化测试

除了手动调用脚本,更推荐使用 pytest 编写可重复执行的自动化测试。这样每次修改代码后,只需运行一条命令即可验证所有接口是否正常。

首先安装 pytest 和 httpx(FastAPI 的测试客户端依赖):

pip install pytest httpx

然后创建 test_main.py 测试文件:

import pytest
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)


def test_create_todo():
    """测试创建待办事项"""
    resp = client.post("/todos", json={
   
   
        "title": "学习 pytest",
        "description": "编写自动化测试",
    })
    assert resp.status_code == 201
    data = resp.json()
    assert data["title"] == "学习 pytest"
    assert data["completed"] is False
    assert "id" in data


def test_list_todos():
    """测试查询待办事项列表"""
    # 先创建一条数据
    client.post("/todos", json={
   
   "title": "测试列表"})

    resp = client.get("/todos")
    assert resp.status_code == 200
    assert isinstance(resp.json(), list)
    assert len(resp.json()) >= 1


def test_get_todo_not_found():
    """测试查询不存在的待办事项返回 404"""
    resp = client.get("/todos/99999")
    assert resp.status_code == 404
    assert resp.json()["code"] == 404


def test_update_todo():
    """测试更新待办事项"""
    # 先创建一条数据
    create_resp = client.post("/todos", json={
   
   "title": "待更新"})
    todo_id = create_resp.json()["id"]

    resp = client.put(f"/todos/{
     
     todo_id}", json={
   
   
        "title": "已更新",
        "completed": True,
    })
    assert resp.status_code == 200
    assert resp.json()["title"] == "已更新"
    assert resp.json()["completed"] is True


def test_delete_todo():
    """测试删除待办事项"""
    # 先创建一条数据
    create_resp = client.post("/todos", json={
   
   "title": "待删除"})
    todo_id = create_resp.json()["id"]

    resp = client.delete(f"/todos/{
     
     todo_id}")
    assert resp.status_code == 204

    # 再次查询应返回 404
    resp = client.get(f"/todos/{
     
     todo_id}")
    assert resp.status_code == 404


def test_validation_error():
    """测试参数校验失败返回 422"""
    resp = client.post("/todos", json={
   
   "title": ""})
    assert resp.status_code == 422
    assert resp.json()["code"] == 422

运行测试:

pytest test_main.py -v

输出示例:

============================= test session starts =============================
collected 6 items

test_main.py::test_create_todo PASSED
test_main.py::test_list_todos PASSED
test_main.py::test_get_todo_not_found PASSED
test_main.py::test_update_todo PASSED
test_main.py::test_delete_todo PASSED
test_main.py::test_validation_error PASSED

============================== 6 passed in 0.35s ==============================

测试要点:使用 TestClient 无需启动真实服务器即可模拟请求;每个测试用例相互独立,建议在测试前清空数据库或使用独立测试库;断言时同时检查状态码和响应体,确保接口行为符合预期。

7.5.8 使用 pytest 测试数据库集成

在完成数据库改造后,我们需要验证 CRUD 接口与 SQLite 的集成是否正常。下面是一个针对数据库版本的 pytest 测试脚本:

# test_db_integration.py
import os
import tempfile
import pytest
from fastapi.testclient import TestClient

# 使用临时数据库,避免污染开发数据
os.environ["DATABASE_URL"] = f"sqlite:///{
     
     tempfile.mkdtemp()}/test.db"

from main import app, Base, engine, SessionLocal, TodoDB

# 创建测试表
Base.metadata.create_all(bind=engine)

client = TestClient(app)


@pytest.fixture(autouse=True)
def clean_db():
    """每个测试用例前清空数据库"""
    db = SessionLocal()
    db.query(TodoDB).delete()
    db.commit()
    db.close()
    yield


def test_create_todo_persists_to_db():
    """创建待办事项后,数据应写入数据库"""
    resp = client.post("/todos", json={
   
   "title": "数据库测试", "description": "验证持久化"})
    assert resp.status_code == 201
    data = resp.json()
    assert data["title"] == "数据库测试"

    # 直接从数据库验证
    db = SessionLocal()
    todo = db.query(TodoDB).filter(TodoDB.id == data["id"]).first()
    assert todo is not None
    assert todo.title == "数据库测试"
    db.close()


def test_list_todos_reads_from_db():
    """列表接口应返回数据库中的数据"""
    # 先创建一条数据
    client.post("/todos", json={
   
   "title": "待查询事项"})

    resp = client.get("/todos")
    assert resp.status_code == 200
    assert len(resp.json()) == 1
    assert resp.json()[0]["title"] == "待查询事项"


def test_update_todo_syncs_to_db():
    """更新接口应同步修改数据库记录"""
    create_resp = client.post("/todos", json={
   
   "title": "原始标题"})
    todo_id = create_resp.json()["id"]

    update_resp = client.put(
        f"/todos/{
     
     todo_id}",
        json={
   
   "title": "更新后的标题", "completed": True},
    )
    assert update_resp.status_code == 200
    assert update_resp.json()["title"] == "更新后的标题"

    # 验证数据库中的记录已更新
    db = SessionLocal()
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    assert todo.title == "更新后的标题"
    assert todo.completed is True
    db.close()


def test_delete_todo_removes_from_db():
    """删除接口应移除数据库记录"""
    create_resp = client.post("/todos", json={
   
   "title": "待删除事项"})
    todo_id = create_resp.json()["id"]

    delete_resp = client.delete(f"/todos/{
     
     todo_id}")
    assert delete_resp.status_code == 204

    # 验证数据库中的记录已删除
    db = SessionLocal()
    todo = db.query(TodoDB).filter(TodoDB.id == todo_id).first()
    assert todo is None
    db.close()

运行测试:

pytest test_db_integration.py -v

测试要点:使用 TestClient 模拟真实 HTTP 请求,无需启动服务器;通过 autouse fixture 在每个用例前清空数据库,保证测试隔离;测试结束后临时数据库自动清理,不影响开发数据。

7.5.9 使用 pytest 测试部署后的接口

部署完成后,我们同样可以使用 pytest 对线上接口进行冒烟测试,确保服务在云平台上正常运行。下面是一个针对部署环境的测试脚本:

# test_deployed_api.py
"""
部署环境冒烟测试
验证线上 API 的核心功能是否正常
"""
import os
import requests
import pytest

# 从环境变量读取线上地址,本地测试时使用默认值
BASE_URL = os.getenv("DEPLOYED_API_URL", "https://your-app.railway.app")


@pytest.fixture(scope="module")
def base_url():
    """返回线上 API 地址"""
    return BASE_URL


def test_health_check(base_url):
    """健康检查接口应返回 200"""
    resp = requests.get(f"{
     
     base_url}/health", timeout=10)
    assert resp.status_code == 200
    data = resp.json()
    assert data["status"] == "ok"


def test_create_todo(base_url):
    """线上创建待办事项"""
    resp = requests.post(
        f"{
     
     base_url}/todos",
        json={
   
   "title": "部署冒烟测试", "description": "验证线上创建功能"},
        timeout=10,
    )
    assert resp.status_code == 201
    data = resp.json()
    assert data["title"] == "部署冒烟测试"
    return data["id"]


def test_list_todos(base_url):
    """线上查询列表"""
    resp = requests.get(f"{
     
     base_url}/todos", timeout=10)
    assert resp.status_code == 200
    assert isinstance(resp.json(), list)


def test_get_todo(base_url):
    """线上查询单个待办事项"""
    # 先创建一个
    create_resp = requests.post(
        f"{
     
     base_url}/todos",
        json={
   
   "title": "查询测试"},
        timeout=10,
    )
    todo_id = create_resp.json()["id"]

    resp = requests.get(f"{
     
     base_url}/todos/{
     
     todo_id}", timeout=10)
    assert resp.status_code == 200
    assert resp.json()["id"] == todo_id


def test_update_todo(base_url):
    """线上更新待办事项"""
    create_resp = requests.post(
        f"{
     
     base_url}/todos",
        json={
   
   "title": "更新前", "completed": False},
        timeout=10,
    )
    todo_id = create_resp.json()["id"]

    resp = requests.put(
        f"{
     
     base_url}/todos/{
     
     todo_id}",
        json={
   
   "title": "更新后", "completed": True},
        timeout=10,
    )
    assert resp.status_code == 200
    data = resp.json()
    assert data["title"] == "更新后"
    assert data["completed"] is True


def test_delete_todo(base_url):
    """线上删除待办事项"""
    create_resp = requests.post(
        f"{
     
     base_url}/todos",
        json={
   
   "title": "待删除"},
        timeout=10,
    )
    todo_id = create_resp.json()["id"]

    resp = requests.delete(f"{
     
     base_url}/todos/{
     
     todo_id}", timeout=10)
    assert resp.status_code == 204


def test_404_for_missing_todo(base_url):
    """查询不存在的待办事项应返回 404"""
    resp = requests.get(f"{
     
     base_url}/todos/999999", timeout=10)
    assert resp.status_code == 404
    data = resp.json()
    assert data["code"] == 404

运行方式:

# 本地测试
pytest test_deployed_api.py -v

# 指定线上地址测试
DEPLOYED_API_URL="https://your-app.railway.app" pytest test_deployed_api.py -v

部署测试要点:冒烟测试覆盖健康检查与核心 CRUD 流程,确保线上服务可用;通过环境变量注入线上地址,避免把测试地址硬编码;建议在 CI/CD 流水线中部署完成后自动执行冒烟测试,快速发现回归问题。

7.5.10 使用 curl 脚本进行端到端冒烟测试

除了使用 pytest 编写单元测试,我们还可以编写一个轻量级的 shell 脚本,对部署后的 API 进行快速的端到端冒烟测试。这种方式非常适合在 CI/CD 流水线中作为部署后的健康检查步骤。

#!/usr/bin/env bash
# smoke_test.sh - API 冒烟测试脚本
# 用法: ./smoke_test.sh [BASE_URL]
# 示例: ./smoke_test.sh http://127.0.0.1:8000

set -e  # 任何命令失败则立即退出

BASE_URL="${1:-http://127.0.0.1:8000}"
PASS=0
FAIL=0

# 辅助函数:检查 HTTP 状态码
check_status() {
   
   
  local expected="$1"
  local actual="$2"
  local description="$3"

  if [ "$expected" -eq "$actual" ]; then
    echo "✅ PASS: $description (HTTP $actual)"
    PASS=$((PASS + 1))
  else
    echo "❌ FAIL: $description (期望 HTTP $expected, 实际 HTTP $actual)"
    FAIL=$((FAIL + 1))
  fi
}

echo "========================================"
echo "开始 API 冒烟测试: $BASE_URL"
echo "========================================"

# 1. 健康检查
echo ""
echo "--- 1. 健康检查 ---"
HEALTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/health")
check_status 200 "$HEALTH_CODE" "健康检查接口"

# 2. 创建待办事项
echo ""
echo "--- 2. 创建待办事项 ---"
CREATE_RESP=$(curl -s -X POST "$BASE_URL/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "冒烟测试任务", "description": "由脚本自动创建"}')
CREATE_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$BASE_URL/todos" \
  -H "Content-Type: application/json" \
  -d '{"title": "冒烟测试任务", "description": "由脚本自动创建"}')
check_status 201 "$CREATE_CODE" "创建待办事项"

# 提取新创建的 ID(使用 Python 解析 JSON)
TODO_ID=$(echo "$CREATE_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])" 2>/dev/null || echo "1")
echo "    创建成功,新 ID = $TODO_ID"

# 3. 查询列表
echo ""
echo "--- 3. 查询待办事项列表 ---"
LIST_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/todos")
check_status 200 "$LIST_CODE" "查询待办事项列表"

# 4. 查询单个
echo ""
echo "--- 4. 查询单个待办事项 ---"
GET_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/todos/$TODO_ID")
check_status 200 "$GET_CODE" "查询单个待办事项 (ID=$TODO_ID)"

# 5. 更新待办事项
echo ""
echo "--- 5. 更新待办事项 ---"
UPDATE_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT "$BASE_URL/todos/$TODO_ID" \
  -H "Content-Type: application/json" \
  -d '{"title": "冒烟测试任务(已更新)", "completed": true}')
check_status 200 "$UPDATE_CODE" "更新待办事项 (ID=$TODO_ID)"

# 6. 删除待办事项
echo ""
echo "--- 6. 删除待办事项 ---"
DELETE_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE "$BASE_URL/todos/$TODO_ID")
check_status 204 "$DELETE_CODE" "删除待办事项 (ID=$TODO_ID)"

# 7. 验证删除后返回 404
echo ""
echo "--- 7. 验证删除后返回 404 ---"
NOT_FOUND_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/todos/$TODO_ID")
check_status 404 "$NOT_FOUND_CODE" "删除后查询应返回 404"

echo ""
echo "========================================"
echo "测试完成: 通过 $PASS 项, 失败 $FAIL 项"
echo "========================================"

# 如果有失败项,以非零状态码退出(供 CI 判断)
if [ "$FAIL" -gt 0 ]; then
  exit 1
fi

运行方式:

# 给脚本添加执行权限
chmod +x smoke_test.sh

# 本地测试
./smoke_test.sh http://127.0.0.1:8000

# 测试部署后的线上环境
./smoke_test.sh https://my-api.onrender.com

冒烟测试要点:脚本覆盖了 CRUD 全流程和健康检查,任何一步失败都会以非零状态码退出;set -e 确保中间步骤失败时立即终止,避免误报;建议将该脚本接入 CI/CD 流水线,在每次部署后自动执行,快速发现回归问题。

7.5.11 使用 pytest 测试完整 CRUD 流程

除了针对单个接口的测试,我们还可以编写一个覆盖完整 CRUD 流程的端到端测试,验证「创建 → 查询 → 更新 → 删除」全链路是否正常。下面给出一个完整的测试脚本:

# test_crud_flow.py
"""
完整 CRUD 流程测试
覆盖创建、查询、更新、删除的端到端验证
"""
import requests
import pytest

BASE_URL = "http://127.0.0.1:8000"


@pytest.fixture
FastAPI快速搭建RESTful API:7分钟实战指南 RESTful API是现代Web开发的核心技术,它基于HTTP协议实现资源操作的标准接口。FastAPI作为Python生态中高性能的Web框架,通过类型提示和Pydantic模型自动生成OpenAPI文档,大幅提升开发效率。其异步特性配合Starlette底层支持,使请求处理速度达到传统框架的3倍。在微服务架构和前后端分离项目中,FastAPI能快速实现商品管理、用户系统等常见业务场景的API开发。本教程结合CRUD操作和Swagger UI集成,演示如何用Python 3.10+和Pydantic在7 阅读详情

相关推荐

FastAPI与MySQL数据库连接实战指南

在现代Web开发中,数据库连接是后端系统的核心组件之一。关系型数据库通过SQL语言提供了强大的数据操作能力,而ORM技术则进一步简化了数据库操作。FastAPI作为高性能Python框架,结合MySQL数据库能够构建出响应迅速、类型安全的API服务。通过SQLAlchemy这样的ORM工具,开发者可以以面向对象的方式操作数据库,同时享受连接池、事务管理等高级特性。这种技术组合特别适合需要快速迭代的中大型项目,如电商平台、内容管理系统等。实战中需要注意连接池配置、异步支持和密码安全等关键点,以确保系统的稳定性

381

FastAPI高性能Web框架:从类型安全到高并发API开发实战

现代Web开发中,API框架的选择直接影响项目的开发效率和运行性能。基于Python类型提示的FastAPI框架,通过自动生成OpenAPI文档和全面支持异步编程,实现了类型安全与高性能的完美结合。其核心原理是利用Pydantic进行数据验证,配合ASGI服务器实现高并发处理,技术价值在于显著提升开发效率并降低维护成本。在微服务架构、实时应用和高并发API等场景下,FastAPI展现出接近Node.js和Go的性能水平,单机可轻松支撑每秒数千请求。本文重点探讨如何通过类型检查和并发性能优化,构建稳定高效的W

weixin_30521161的博客 351

Python 领域 FastAPI 实战:从 0 到 1 构建 API

本文旨在为具备 Python 基础的开发者提供一套完整的 FastAPI 实战指南,从环境搭建到复杂 API 系统开发,覆盖技术选型、架构设计、代码实现、测试部署全流程。通过具体案例演示,解析 FastAPI 在 RESTful API 开发中的核心优势,包括高性能异步处理、自动生成交互式文档、强类型校验等特性的实际应用。核心概念:解析 FastAPI 架构、异步模型、OpenAPI 规范技术原理:通过代码示例讲解路由、依赖注入、数据验证项目实战:分步实现用户管理系统,包含数据库交互和异常处理。

AI Python 编程之道的博客 1616

从零到一:揭秘BlueROV2机械爪背后的Pixhawk舵机控制艺术

本文详细解析了如何基于Pixhawk飞控系统为BlueROV2水下机器人构建可靠的舵机控制系统。从硬件选型、ArduSub固件配置到供电安全与故障排除,全面揭秘机械爪背后的控制艺术,为水下机器人爱好者提供实用指南。

ol78901234的博客 243

Android架构中UseCase的设计原则与实战解析

本文深入解析Android架构中UseCase的设计原则与实战应用,探讨其在Domain Layer中的核心作用。通过对比ViewModel的臃肿问题,详细阐述UseCase的无状态原则、单一职责边界等设计铁律,并提供多种实现方式的代码示例,帮助开发者构建更清晰、高效的Android应用架构。

weixin_28432777的博客 431

从零搭建SPI通信系统:AXI Quad SPI IP核在Xilinx平台上的完整配置流程

本文详细介绍了在Xilinx平台上使用AXI Quad SPI IP核从零搭建SPI通信系统的完整配置流程。涵盖IP核架构解析、Vivado环境配置、寄存器编程实战及性能优化策略,帮助开发者快速实现高速稳定的SPI通信,特别适用于嵌入式系统开发中的外围设备连接需求。

hh234的博客 1053

学术类PPT制作总结

本文分享了科技类PPT的制作技巧,希望相关的信息能帮助到读者。

奇妙水果的技术博客,欢迎交流技术~ 2884

Coze知识库搭建:智能化管理与高效检索实践

知识管理系统是现代企业信息资产管理的核心基础设施,其核心原理是通过结构化存储和智能检索技术实现知识的快速流转。在技术实现上,基于NLP的语义理解引擎和知识图谱构建是关键,能够显著提升检索准确率和知识关联性。以Coze平台为例,其针对中文场景优化的分词算法和多模态处理能力,可有效解决技术文档管理中的术语识别和格式兼容问题。这类系统在开发团队知识沉淀、新员工培训等场景中价值显著,特别是当集成RBAC权限模型和自动化备份策略后,既能保障信息安全,又能实现持续的知识迭代。通过合理配置同义词扩展和权重调整,可使搜索命

weixin_29041195的博客 262

system_server_lowmem真的是内存不足吗?

Android系统中的system_server_lowmem真的表示系统的内存遇到瓶颈了吗?以及遇到这类日志该如何分析系统的内存,本文会回答这个问题。

jidapenglin28的专栏 513

基于STM32F1XX 、HAL库的LTR-303ALS-01 环境光传感器驱动应用C语言程序设计

printf("制造商ID: 0x%02X\r\n", LTR303_ReadManufacID());printf("器件ID: 0x%02X\r\n", LTR303_ReadPartID());LTR-303ALS-01是一款数字环境光传感器(ALS),由Lite-On公司生产。printf("LTR-303ALS初始化失败!printf("LTR-303ALS初始化成功!printf("数据无效\r\n");* @param mode: 0-待机, 1-激活。

PP_Cottage的博客 1033

# YOLOv11改进系列 | 引入仓库自研C3k2_EIEM模块,Sobel边缘分支增强检测细节表达

文章摘要: YOLOv11-seg改进中提出的C3k2_EIEM模块通过引入边缘信息增强机制,显著提升了模型对轮廓和纹理的感知能力。该模块在保持原有C3k2结构的基础上,创新性地结合了Sobel边缘检测分支与常规卷积分支的双路特征融合。核心改进包括:(1)固定Sobel算子提供稳定的梯度响应;(2)并行处理边缘与纹理特征;(3)通过1x1卷积实现特征融合。实验显示,改进后模型参数量增加14.16%,计算量提升12.5%,但显著增强了在复杂边界场景下的分割精度。这种设计特别适用于需要精细轮廓识别的任务,如商品

qq_44224801的博客 95

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

上一篇: AI 插件助手
下一篇: 27届计算机本科生优秀毕业设计选题推荐(附技术栈与创新点)
system_error_x
博客等级 码龄3年 17粉丝 54原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值