首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >FastAPI HTTP 状态码与请求异常处理避坑全解:从 404 到 500,4 种自定义异常方式全演示

FastAPI HTTP 状态码与请求异常处理避坑全解:从 404 到 500,4 种自定义异常方式全演示

作者头像
玄同765
发布2026-01-14 14:15:29
发布2026-01-14 14:15:29
680
举报
在这里插入图片描述
在这里插入图片描述

【个人主页:】

大语言模型(LLM)开发工程师中国传媒大学·数字媒体技术(智能交互与游戏设计) 深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调 技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️ 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案 专栏传送门:LLM大模型开发 项目实战指南Python 从真零基础到纯文本 LLM 全栈实战​​​​​从零学 SQL + 大模型应用落地大模型开发小白专属:从 0 入门 Linux&Shell 「让AI交互更智能,让技术落地更高效」 欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!

摘要

你是不是写 FastAPI 接口时,遇到查询用户不存在只会返回默认的 HTML,或者直接抛 Internal Server Error?这篇详解 HTTP 状态码的分类、含义、FastAPI 的 4 种请求异常处理方式,配合学生管理系统的代码示例和对比表格,帮你优雅地处理各种异常场景。


一、引言:为什么要重视状态码和异常处理?

在 API 接口开发的入门阶段,很多开发者会忽略 HTTP 状态码和请求异常处理,比如查询用户不存在时返回 200+{data:null},或者遇到数据库连接失败时直接抛出默认的 500 Internal Server Error。这种做法会导致以下问题:

  1. 用户体验不好:前端开发者无法根据状态码快速判断请求是否成功;
  2. 代码规范不好:不符合 RESTful API 设计风格;
  3. 调试困难:默认的 500 错误没有包含详细的错误信息;
  4. 安全风险:默认的 500 错误可能会泄露服务器的敏感信息。

因此,我们需要重视 HTTP 状态码和请求异常处理,优雅地处理各种异常场景。


二、HTTP 状态码扫盲

HTTP 状态码是服务器对客户端请求的响应状态的数字代码。HTTP 状态码由三个数字组成,分为以下几类:

  1. 1xx(信息状态码):表示服务器已接收请求,正在处理;
  2. 2xx(成功状态码):表示请求已成功处理;
  3. 3xx(重定向状态码):表示请求需要进一步操作才能完成;
  4. 4xx(客户端错误状态码):表示客户端发送的请求有错误;
  5. 5xx(服务器错误状态码):表示服务器在处理请求时出现错误。
2.1 常用 HTTP 状态码

以下是 FastAPI 开发中最常用的 HTTP 状态码:

状态码

类型

含义

常见场景

200

成功

OK

请求已成功处理,返回请求的数据

201

成功

Created

请求已成功处理,创建了新的资源

400

客户端错误

Bad Request

请求参数有错误,如缺少必填参数、参数类型错误、参数格式错误

401

客户端错误

Unauthorized

用户未登录或登录状态已过期

403

客户端错误

Forbidden

用户已登录,但无权限访问该资源

404

客户端错误

Not Found

请求的资源不存在

500

服务器错误

Internal Server Error

服务器在处理请求时出现错误,如数据库连接失败、代码逻辑错误

503

服务器错误

Service Unavailable

服务器暂时不可用,如服务器正在维护、服务器负载过高


三、FastAPI 的请求异常处理方式

FastAPI 提供了 4 种请求异常处理方式:

  1. raise HTTPException:直接抛出 HTTPException 异常;
  2. 使用 HTTPException 的 status_code 参数:设置 HTTPException 的 status_code 参数;
  3. 自定义异常类继承 HTTPException:创建自定义异常类继承 HTTPException;
  4. 全局异常处理(Exception Handler):使用 @app.exception_handler 装饰器捕获并处理异常。
3.1 方式一:raise HTTPException

语法

代码语言:javascript
复制
from fastapi import HTTPException, status

def function_name():
    ...
    if condition:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="请求的资源不存在")
    ...

代码示例(学生管理系统)

代码语言:javascript
复制
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.models.students import Student
from app.schemas.students import StudentInfo

router = APIRouter(prefix="/students", tags=["Students"])

@router.get("/{student_id}", response_model=StudentInfo)
def get_student_info(student_id: int = 101, db: Session = Depends(get_db)):
    """获取单个学生信息"""
    student = db.query(Student).filter(Student.id == student_id).first()
    if not student:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="学生不存在")
    return student

测试:打开浏览器,访问 http://127.0.0.1:8000/docs,点击/students/{student_id}接口旁边的Try it out按钮,输入100,点击Execute,返回以下 JSON 响应:

代码语言:javascript
复制
{"detail":"学生不存在"}
3.2 方式二:使用 HTTPException 的 status_code 参数

除了使用 FastAPI 内置的 status 模块,我们还可以直接使用数字作为 status_code 参数。

代码示例(学生管理系统)

代码语言:javascript
复制
@router.get("/{student_id}", response_model=StudentInfo)
def get_student_info(student_id: int = 101, db: Session = Depends(get_db)):
    """获取单个学生信息"""
    student = db.query(Student).filter(Student.id == student_id).first()
    if not student:
        raise HTTPException(status_code=404, detail="学生不存在")
    return student

测试:结果与方式一相同。

3.3 方式三:自定义异常类继承 HTTPException

如果我们需要处理的异常场景比较复杂,可以创建自定义异常类继承 HTTPException。

代码示例(学生管理系统)

代码语言:javascript
复制
from fastapi import HTTPException, status

class StudentNotFoundException(HTTPException):
    """学生不存在异常"""
    def __init__(self, student_id: int):
        detail = f"学生ID为{student_id}的学生不存在"
        super().__init__(status_code=status.HTTP_404_NOT_FOUND, detail=detail)

class StudentAlreadyExistsException(HTTPException):
    """学生已存在异常"""
    def __init__(self, student_no: str):
        detail = f"学号为{student_no}的学生已存在"
        super().__init__(status_code=status.HTTP_400_BAD_REQUEST, detail=detail)

在 API 接口中使用自定义异常类:

代码语言:javascript
复制
from app.schemas.students import StudentCreate
from app.services.students_service import create_student, get_student_by_no
from app.utils.exceptions import StudentAlreadyExistsException, StudentNotFoundException

@router.post("/", response_model=StudentInfo)
def create_student_info(student: StudentCreate, db: Session = Depends(get_db)):
    """创建学生信息"""
    existing_student = get_student_by_no(db, student.student_no)
    if existing_student:
        raise StudentAlreadyExistsException(student.student_no)
    new_student = create_student(db, student)
    return new_student

@router.get("/{student_id}", response_model=StudentInfo)
def get_student_info(student_id: int = 101, db: Session = Depends(get_db)):
    """获取单个学生信息"""
    student = db.query(Student).filter(Student.id == student_id).first()
    if not student:
        raise StudentNotFoundException(student_id)
    return student

测试:打开浏览器,访问 http://127.0.0.1:8000/docs,点击/students接口旁边的Try it out按钮,输入以下 JSON 数据:

代码语言:javascript
复制
{
  "student_no": "20250001",
  "name": "张三",
  "gender": "男",
  "birthdate": "2005-01-01",
  "phone": "13800138001",
  "email": "zhangsan@example.com",
  "address": "北京市海淀区"
}

点击Execute,返回以下 JSON 响应:

代码语言:javascript
复制
{"detail":"学号为20250001的学生已存在"}
3.4 方式四:全局异常处理(Exception Handler)

如果我们需要处理全局的异常场景,可以使用 @app.exception_handler 装饰器捕获并处理异常。

代码示例(学生管理系统)

代码语言:javascript
复制
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from app.utils.exceptions import StudentNotFoundException, StudentAlreadyExistsException

app = FastAPI(title="学生管理系统", version="1.0.0", description="一个基于FastAPI的学生管理系统")

@app.exception_handler(StudentNotFoundException)
async def student_not_found_exception_handler(request: Request, exc: StudentNotFoundException):
    """学生不存在异常的全局处理函数"""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "code": exc.status_code,
            "message": exc.detail,
            "path": request.url.path
        }
    )

@app.exception_handler(StudentAlreadyExistsException)
async def student_already_exists_exception_handler(request: Request, exc: StudentAlreadyExistsException):
    """学生已存在异常的全局处理函数"""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "code": exc.status_code,
            "message": exc.detail,
            "path": request.url.path
        }
    )

@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    """全局异常处理函数"""
    return JSONResponse(
        status_code=500,
        content={
            "code": 500,
            "message": "服务器内部错误",
            "path": request.url.path,
            "error": str(exc)
        }
    )

测试:打开浏览器,访问 http://127.0.0.1:8000/docs,点击/students/{student_id}接口旁边的Try it out按钮,输入100,点击Execute,返回以下 JSON 响应:

代码语言:javascript
复制
{"code":404,"message":"学生ID为100的学生不存在","path":"/students/100"}

四、常用异常场景与处理代码示例(学生管理系统)
4.1 查询用户不存在 → 404

代码示例

代码语言:javascript
复制
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.models.users import User
from app.schemas.users import UserInfo
from app.utils.exceptions import UserNotFoundException

router = APIRouter(prefix="/users", tags=["Users"])

@router.get("/{user_id}", response_model=UserInfo)
def get_user_info(user_id: int, db: Session = Depends(get_db)):
    """获取单个用户信息"""
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise UserNotFoundException(user_id)
    return user
4.2 用户名已存在 → 400

代码示例

代码语言:javascript
复制
from app.schemas.users import UserCreate
from app.services.users_service import create_user, get_user_by_username
from app.utils.exceptions import UserAlreadyExistsException

@router.post("/", response_model=UserInfo)
def create_user_info(user: UserCreate, db: Session = Depends(get_db)):
    """创建用户信息"""
    existing_user = get_user_by_username(db, user.username)
    if existing_user:
        raise UserAlreadyExistsException(user.username)
    new_user = create_user(db, user)
    return new_user
4.3 密码错误 → 401

代码示例

代码语言:javascript
复制
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.core.security import authenticate_user, create_access_token
from app.schemas.auth import Token
from app.utils.exceptions import InvalidCredentialsException

router = APIRouter(prefix="/auth", tags=["Auth"])

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")

@router.post("/login", response_model=Token)
def login(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    """用户登录"""
    user = authenticate_user(db, form_data.username, form_data.password)
    if not user:
        raise InvalidCredentialsException()
    access_token = create_access_token(data={"sub": user.username})
    return {"access_token": access_token, "token_type": "bearer"}
4.4 无权限访问 → 403

代码示例

代码语言:javascript
复制
from app.core.dependencies import get_current_active_admin_user

@router.get("/", response_model=list[UserInfo])
def get_users_info(page: int = 1, page_size: int = 10, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_admin_user)):
    """获取用户列表(需要管理员权限)"""
    # 查询用户列表的代码
    return []
4.5 数据库连接失败 → 500

代码示例

代码语言:javascript
复制
from app.utils.exceptions import DatabaseConnectionException

@app.get("/")
def test_database_connection(db: Session = Depends(get_db)):
    """测试数据库连接"""
    try:
        # 执行一个简单的查询
        db.execute("SELECT 1")
        return {"message": "数据库连接成功"}
    except Exception as e:
        raise DatabaseConnectionException(str(e))

五、常见问题与解决方案
5.1 状态码使用错误

问题:使用错误的 HTTP 状态码,如查询用户不存在时返回 400 而不是 404。

解决方案:根据 HTTP 状态码的分类和含义,选择合适的 HTTP 状态码。

5.2 错误信息不详细

问题:错误信息不详细,如只返回 “请求失败” 而不是 “学生 ID 为 100 的学生不存在”。

解决方案:在 HTTPException 或自定义异常类的 detail 参数中添加详细的错误信息。

5.3 全局异常处理覆盖范围不完整

问题:全局异常处理覆盖范围不完整,如没有处理特定的异常类。

解决方案:添加更多的 @app.exception_handler 装饰器,处理特定的异常类。

5.4 敏感信息泄露

问题:全局异常处理时泄露服务器的敏感信息,如数据库连接失败时返回完整的错误堆栈。

解决方案:在生产环境中,不要在全局异常处理中返回完整的错误堆栈,只返回简单的错误信息。


六、总结与展望
6.1 总结

通过以上步骤,我们详解了 HTTP 状态码的分类、含义、FastAPI 的 4 种请求异常处理方式,配合学生管理系统的代码示例和对比表格,帮你优雅地处理各种异常场景。

6.2 展望

未来,我们可以对 FastAPI 的请求异常处理进行以下优化:

  • 使用错误码系统:创建一个错误码系统,将错误信息与错误码对应起来;
  • 使用日志管理工具:将异常信息记录到日志文件中,方便排查问题;
  • 使用监控工具:使用监控工具(如 Prometheus 和 Grafana)监控异常的发生频率;
  • 使用邮件告警工具:当异常发生时,发送邮件告警给开发者。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 摘要
    • 一、引言:为什么要重视状态码和异常处理?
    • 二、HTTP 状态码扫盲
      • 2.1 常用 HTTP 状态码
    • 三、FastAPI 的请求异常处理方式
      • 3.1 方式一:raise HTTPException
      • 3.2 方式二:使用 HTTPException 的 status_code 参数
      • 3.3 方式三:自定义异常类继承 HTTPException
      • 3.4 方式四:全局异常处理(Exception Handler)
    • 四、常用异常场景与处理代码示例(学生管理系统)
      • 4.1 查询用户不存在 → 404
      • 4.2 用户名已存在 → 400
      • 4.3 密码错误 → 401
      • 4.4 无权限访问 → 403
      • 4.5 数据库连接失败 → 500
    • 五、常见问题与解决方案
      • 5.1 状态码使用错误
      • 5.2 错误信息不详细
      • 5.3 全局异常处理覆盖范围不完整
      • 5.4 敏感信息泄露
    • 六、总结与展望
      • 6.1 总结
      • 6.2 展望
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档