FastAPI优雅处理CORS跨域与日志记录的实战指南

 更新时间:2026年02月10日 08:46:07   作者:小庄-Python办公  
在当今的 Web 开发领域,API(应用程序接口)是连接前后端以及不同服务之间的桥梁,FastAPI 的核心优势在于其高性能和开发效率的完美结合,下面我们就来看看FastAPI如何处理CORS跨域与日志记录吧

第一章:为什么 FastAPI 成为现代 Python Web 开发的首选?

在当今的 Web 开发领域,API(应用程序接口)是连接前后端以及不同服务之间的桥梁。Python 作为一门简洁且强大的语言,拥有众多优秀的 Web 框架,而 FastAPI 正是近年来异军突起的新星。

FastAPI 的核心优势在于其高性能开发效率的完美结合。它基于 Python 3.6+ 的类型提示(Type Hints),利用 Pydantic 进行数据验证和序列化,不仅保证了代码的可读性,还自动生成了交互式的 API 文档(Swagger UI 和 ReDoc)。

然而,当我们从简单的 “Hello World” 迈向生产级应用时,两个问题几乎是不可避免的:

  • CORS 跨域问题:前端应用(如 React, Vue)在本地开发时调用后端 API,经常会遇到浏览器的跨域错误。
  • 日志记录:在生产环境中,当 API 出现异常或性能瓶颈时,如果没有完善的日志系统,排查问题就像在大海捞针。

本篇文章将深入探讨如何在 FastAPI 中结合 Python 的标准库和中间件机制,优雅且专业地解决这两个核心痛点。

第二章:跨域资源共享 (CORS) 的原理与 FastAPI 实战

什么是 CORS

CORS(Cross-Origin Resource Sharing)是一种基于 HTTP 头的机制,它允许服务器声明哪些源(Origin)有权访问其资源。浏览器出于安全考虑,默认遵循“同源策略”,即如果你的前端运行在 localhost:3000,而后端在 localhost:8000,浏览器会拦截它们之间的请求。

在 FastAPI 中解决 CORS

FastAPI 提供了一个非常便捷的工具 CORSMiddleware 来处理这个问题。它的原理是通过在请求到达你的路由处理函数之前,拦截并添加正确的 HTTP 响应头(如 Access-Control-Allow-Origin)。

实战代码:配置 CORS

不要在每个路由上手动添加 Header,而是使用中间件全局处理。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# 1. 定义允许的源列表
# 在生产环境中,请务必替换为具体的域名,而不是使用 "*" 通配符
# 例如: origins = ["https://your-frontend-domain.com"]
origins = [
    "http://localhost",
    "http://localhost:3000",
    "http://127.0.0.1",
    "http://127.0.0.1:3000",
]

# 2. 添加中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,           # 允许访问的源
    allow_credentials=True,          # 允许携带 Cookie
    allow_methods=["*"],             # 允许的方法 (GET, POST, PUT, DELETE, OPTIONS 等)
    allow_headers=["*"],             # 允许的请求头
)

@app.get("/")
async def root():
    return {"message": "Hello World with CORS enabled!"}

专家提示

  • 安全性警告:请永远不要在生产环境中设置 allow_origins=["*"] 并同时开启 allow_credentials=True。这是浏览器的一个安全限制,两者无法同时生效。正确的做法是明确列出允许的域名。
  • 预检请求 (Preflight):对于复杂的请求(如带有自定义 Header 的 POST 请求),浏览器会先发送一个 OPTIONS 方法的预检请求。CORSMiddleware 会自动处理这些请求,你无需编写额外的路由逻辑。

第三章:构建生产级日志系统

日志是软件的“黑匣子”。在 Python 中,虽然 print 很方便,但它在生产环境中存在诸多缺陷(如无法区分级别、难以定向输出、性能较差)。我们需要使用 Python 内置的 logging 模块来构建结构化的日志系统。

1. 基础配置:格式化与输出

一个良好的日志应该包含时间戳、日志级别、文件位置以及具体的日志信息。

import logging
import sys

# 配置日志格式
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    handlers=[
        logging.StreamHandler(sys.stdout)  # 输出到控制台
    ]
)

logger = logging.getLogger(__name__)

# 测试日志
logger.info("系统启动,日志配置已加载")

2. 进阶:在 FastAPI 中实现请求日志记录

仅仅记录应用启动的日志是不够的。我们需要记录每一个进来的请求(URL、方法、耗时)以及每一个抛出的异常。FastAPI 的事件处理器 (Event Handlers)中间件 (Middleware) 是完成这个任务的最佳搭档。

方案 A:使用中间件记录请求耗时

中间件可以包裹每一个请求,计算处理时间。

import time
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start_time = time.time()
        
        # 处理请求
        response = await call_next(request)
        
        # 计算耗时并记录
        process_time = (time.time() - start_time) * 1000  # 转换为毫秒
        formatted_time = f"{process_time:.2f}ms"
        
        logger.info(
            f"{request.method} {request.url.path} - "
            f"Status: {response.status_code} - "
            f"Duration: {formatted_time}"
        )
        
        return response

方案 B:捕获全局异常

为了防止异常发生时只有默认的报错信息,我们可以注册一个全局异常处理器。

from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse

@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    logger.error(f"HTTPException occurred: {exc.status_code} - {exc.detail} at {request.url.path}")
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail},
    )

将这些组件整合到主应用中:

app = FastAPI()

# 添加 CORS (来自第二章)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], 
    allow_methods=["*"], 
    allow_headers=["*"]
)

# 添加日志中间件
app.add_middleware(LoggingMiddleware)

# ... 路由定义 ...

3. 日志轮转 (Log Rotation)

在生产环境中,日志文件如果不加控制,会迅速占满磁盘空间。Python 的 logging.handlers 模块提供了 RotatingFileHandlerTimedRotatingFileHandler

  • RotatingFileHandler: 按文件大小切割(例如每 10MB 切割一次,保留 5 个备份)。
  • TimedRotatingFileHandler: 按时间切割(例如每天凌晨切割一次)。

代码示例(按大小切割):

from logging.handlers import RotatingFileHandler

file_handler = RotatingFileHandler(
    "app.log", 
    maxBytes=10*1024*1024,  # 10MB
    backupCount=5,          # 保留5个旧文件
    encoding="utf-8"
)
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
logger.addHandler(file_handler)

第四章:终极整合与最佳实践

将 CORS 和日志结合在一起,我们就能构建一个健壮的 FastAPI 应用骨架。以下是完整的生产级模板代码:

import logging
import time
import sys
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

# --- 配置区域 ---

# 1. 日志配置
logger = logging.getLogger("FastAPI_Prod")
logger.setLevel(logging.INFO)

# 控制台输出
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(console_handler)

# 文件输出 (可选,需确保目录存在)
# file_handler = RotatingFileHandler("logs/app.log", maxBytes=10000000, backupCount=5)
# file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
# logger.addHandler(file_handler)

# 2. CORS 配置
origins = [
    "http://localhost:3000",
    # 生产环境域名
    # "https://www.yourdomain.com"
]

# --- 中间件类 ---

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next):
        start_time = time.time()
        response = await call_next(request)
        process_time = (time.time() - start_time) * 1000
        logger.info(f"{request.method} {request.url.path} - {response.status_code} - {process_time:.2f}ms")
        return response

# --- 应用初始化 ---

app = FastAPI(title="Production Ready API", version="1.0.0")

# 注册中间件(注意顺序:CORS 通常建议在较前的位置,但 Logging 可以包裹它)
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
app.add_middleware(LoggingMiddleware)

# --- 异常处理 ---

@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    logger.error(f"Unhandled Exception: {exc}", exc_info=True)
    return JSONResponse(
        status_code=500,
        content={"detail": "Internal Server Error"},
    )

# --- 路由 ---

@app.get("/")
async def root():
    return {"message": "API is running with advanced logging and CORS"}

@app.get("/error")
async def trigger_error():
    # 触发一个异常来测试日志捕获
    raise HTTPException(status_code=400, detail="Bad Request Test")

# --- 启动命令 ---
# uvicorn main:app --reload

最佳实践总结

  • 日志分级:严格区分 DEBUG, INFO, WARNING, ERROR。不要把所有信息都打成 Info,否则在排查问题时会被淹没。
  • 敏感信息脱敏切勿在日志中记录用户的密码、Token 或完整的信用卡号。可以通过中间件过滤掉 Request Body 中的敏感字段,或者在日志格式化时进行正则替换。
  • CORS 的最小权限原则:不要为了方便就允许所有域名访问。在开发阶段可以宽松,但上线前务必锁定 Allowed Origins。
  • 异步友好:FastAPI 是异步框架,确保你的日志操作不会阻塞事件循环(Python 的标准 logging 在大多数情况下是线程安全的,但在极高并发下可以考虑使用异步队列处理日志写入)。

以上就是FastAPI优雅处理CORS跨域与日志记录的实战指南的详细内容,更多关于FastAPI处理CORS跨域的资料请关注脚本之家其它相关文章!

相关文章

  • Python使用struct库的用法小结

    Python使用struct库的用法小结

    struct模块执行Python值和以Python bytes表示的C结构体之间的转换,这可以用于处理存储在文件中或来自网络连接以及其他源的二进制数据,下面介绍下Python使用struct库的用法,感兴趣的朋友一起看看吧
    2022-05-05
  • python清除指定目录内所有文件中script的方法

    python清除指定目录内所有文件中script的方法

    这篇文章主要介绍了python清除指定目录内所有文件中script的方法,涉及Python针对文件、字符串及正则匹配操作的相关技巧,需要的朋友可以参考下
    2015-06-06
  • Django高并发负载均衡实现原理详解

    Django高并发负载均衡实现原理详解

    这篇文章主要介绍了Django高并发负载均衡实现原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • python获取一组汉字拼音首字母的方法

    python获取一组汉字拼音首字母的方法

    这篇文章主要介绍了python获取一组汉字拼音首字母的方法,涉及Python针对汉字操作的相关技巧,需要的朋友可以参考下
    2015-07-07
  • Python玩转加密的技巧【推荐】

    Python玩转加密的技巧【推荐】

    Python 中的一个有用的基本加密库就叫做 cryptography 。这篇文章主要介绍了Python玩转加密的技巧,需要的朋友可以参考下
    2019-05-05
  • python中的 sorted()函数和sort()方法区别

    python中的 sorted()函数和sort()方法区别

    这篇文章主要介绍了python中的 sorted()函数和sort()方法,首先看sort()方法,sort方法只能对列表进行操作,而sorted可用于所有的可迭代对象。具体内容需要的小伙伴可以参考下面章节
    2022-02-02
  • 简单了解Django ContentType内置组件

    简单了解Django ContentType内置组件

    这篇文章主要介绍了简单了解Django ContentType内置组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 使用 setuptools 在 Python 中安装 egg 文件

    使用 setuptools 在 Python 中安装 egg 

    Eggs 是 Python 中以前使用的一种分发格式,它包含特定项目所需的信息,从依赖项到环境变量,在本文中,我们将讨论如何在 Python 中安装 egg 文件,以及可用于实现此操作的工具,感兴趣的朋友一起看看吧
    2023-08-08
  • Python中闭包和自由变量的使用与注意事项

    Python中闭包和自由变量的使用与注意事项

    这篇文章主要给大家介绍了关于Python中闭包和自由变量的相关资料,需要的朋友可以参考下
    2022-03-03
  • Python使用PySimpleGUI和Pygame编写一个MP3播放器

    Python使用PySimpleGUI和Pygame编写一个MP3播放器

    这篇文章主要为大家详细介绍了Python如何使用PySimpleGUI和Pygame编写一个简单的MP3播放器,文中的示例代码讲解详细,感兴趣的小伙伴可以学习一下
    2023-11-11

最新评论