当前位置: 首页 > news >正文

《FastAPI零基础入门与进阶实战》第21篇:告别 /path/ vs /path:静默斜杠修正中间件

系列文章目录

《FastAPI零基础入门与进阶实战》https://blog.csdn.net/sen_shan/category_12950843.html

第20篇:消息管理-封装https://blog.csdn.net/sen_shan/article/details/151829548?spm=1001.2014.3001.5501

文章目录

目录

系列文章目录

文章目录

前言

设计目标

实现原理

接入指南 

路由优先级对照表 

性能与风险

扩展与定制

FAQ 


前言

        在 RESTful 工程实践中,路径末尾斜杠( / )经常成为“同一个资源却出现两个 URL”的元凶: /login  与  /login/  会被浏览器、CDN、搜索引擎视为不同端点,进而带来
                重复 SEO 权重
                缓存命中率下降
                前端  fetch  因 307 往返增加延迟
        FastAPI/Starlette 默认采用“严格匹配”策略,不会自动合并两种写法。
         StripTrailingSlashMiddleware  在“零重定向”的前提下,用内部路径改写的方式,让“带斜杠的无效路径”静默落到“无斜杠的已注册路由”,从而保持 URL 唯一性,避免 307 Temporary Redirect

设计目标

1. 不返回 307,客户端零感知。
2. 仅当“原路径找不到路由”且“去掉斜杠后能找到”时才改写。
3. 已注册  /path/  的路由不受任何影响(优先匹配原路径)。
4. 对任意 HTTP 方法(GET、POST、PUT...)均生效。
5. 代码 ≤ 30 行,零第三方依赖。

实现原理

orig_path = request.url.path
try:request.app.router.resolve(request.scope)   # ①
except Exception:                               # ②if orig_path.endswith("/"):request.scope["path"] = orig_path[:-1]  # ③

① 利用 Starlette 内部  Router.resolve()  进行“预匹配”。
② 匹配失败说明当前路径在路由表中不存在。
③ 改写 ASGI scope 中的  path  字段;FastAPI 后续再按新路径做二次匹配。

接入指南 

步骤 1:将中间件文件放入项目

src/
├── middleware/
│   ├── __init__.py
│   ├── strip_trailing_slash.py
│   └── cors_config.py          # 存放 CORS 配置
 

步骤 2:strip_trailing_slash

from starlette.responses import RedirectResponse
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.routing import NoMatchFound
from fastapi import Request# 关键:添加自动斜杠重定向中间件
class StripTrailingSlashMiddleware(BaseHTTPMiddleware):"""中间件用于处理路径末尾斜杠问题当请求路径以斜杠结尾但无法匹配到对应路由时,自动去除末尾斜杠并重新尝试路由匹配"""async def dispatch(self, request: Request, call_next):orig_path = request.url.pathif orig_path == "/":return await call_next(request)try:# 尝试直接解析当前请求路径request.app.router.resolve(request.scope)return await call_next(request)except Exception:# 如果路由解析失败且路径以斜杠结尾if orig_path.endswith("/"):# 去掉末尾的 /new_path = request.url.path.rstrip("/")# 直接修改 scope,FastAPI 会用这个新路径去找路由request.scope["path"] = new_pathreturn await call_next(request)

步骤 3:cors_config

# CORS 配置
# 定义允许的跨域源列表
origins = ["http://localhost",         # 允许来自 http://localhost 的请求"http://localhost:8080",    # 允许来自 http://localhost:8080 的请求"http://localhost:5173",    # 允许来自 http://localhost:5173 的请求"https://example.com",      # 允许来自 https://example.com 的请求
]# CORS 中间件配置参数
cors_config = {"allow_origins": origins,"allow_credentials": True,"allow_methods": ["*"],"allow_headers": ["*"],
}

把原来Main.py中以下信息整合到cors_config中

# 定义允许的跨域源列表
origins = ["http://localhost",  # 允许来自 http://localhost 的请求"http://localhost:8080",  # 允许来自 http://localhost:8080 的请求"http://localhost:5173",  # 允许来自 http://localhost:5173 的请求"https://example.com",  # 允许来自 https://example.com 的请求
]# 添加 CORS 中间件
app.add_middleware(CORSMiddleware,allow_origins=origins,  # 允许的源列表allow_credentials=True,  # 允许携带身份凭证(如 Cookies)allow_methods=["*"],  # 允许所有 HTTP 方法allow_headers=["*"],  # 允许所有请求头
)

步骤4:在主程序导入并注册

from src.middleware.strip_trailing_slash import StripTrailingSlashMiddleware
from src.middleware.cors_config import cors_config# 注册中间件
app.add_middleware(StripTrailingSlashMiddleware)
app.add_middleware(CORSMiddleware, **cors_config)

步骤5:验证

# 原路由只注册了 /login
http://127.0.0.1:8080/login/   # HTTP 200,无 307
http://127.0.0.1:8080/login    # HTTP 200,行为不变

步骤6:取消router中最后的斜杆

        避免无法正确的访问,取消所有router中最后的斜杆

路由优先级对照表 

=================== =================== ========================
请求路径            已注册路由          内部结果
=================== =================== ========================
 /login/                  /login                 通过
 /login                   /login                 通过
 /login/                  /login/                 通过

/login                   /login/                不通过
=================== =================== ========================

性能与风险

每次请求仅多一次  router.resolve()  调用,复杂度 O(1),压测损耗 < 1 %。
仅在“原路径 404”时才会进入分支,正常请求无额外开销。
不会修改 query string、body、headers。

扩展与定制

1. 仅对 POST 生效
 在  dispatch  开头加

if request.method != "POST":return await call_next(request)

2. 反向逻辑:补斜杠

        把  rstrip("/")  换成  path + "/"  即可实现“强制带斜杠”策略。避免死循环不能写取消斜杆又增加斜杠。

FAQ 

Q1: 会不会把 WebSocket 路径也改掉?
A: WebSocket 握手路径同样经过  router.resolve() ,逻辑一致,安全。
Q2: 如果同时用了  CORSMiddleware ,顺序如何?
A:  StripTrailingSlashMiddleware  应放在最外层(先注册),确保路径修正早于 CORS 判断。

Q3:是否可用其他重定向中间件

A:starlette可以实现,但是在实际操作中,未达到预期才写了一个中间件。

Q4:若不写中间件,如何实现?

A:路由写2个

@app.post("/login")
@app.post("/login/")
async def regLogin(login_data: login_manager.LoginRequest,db: Session = Depends(get_db),app_manager: Optional[dict] = Depends(dependencies.auth_api_key)):retMes = login.login(db, login_data, app_manager)# print(login.loginInfo.get())return retMes  # {"access_token": login_data, "app_id": app_id}

http://www.dtcms.com/a/495540.html

相关文章:

  • Sherpa 语音识别工具链安装指南(Linux CPU 版)
  • 布林带中轨斜率的计算方法并判断趋势强度
  • 【小白笔记】torch.Tensor 类的实例
  • 俄语网站开发登录信产部网站
  • 学院门户网站建设自己在线制作logo免费生成器
  • 操作系统——进程管理
  • 在docker运行ros及其可视化
  • Python使用 pandas操作Excel文件并新增列数据
  • 宝塔面板点击ssl证书报错:出错了,面板运行时发生错误!ModuleNotFoundError: No module named ‘OpenSSL‘
  • Django与Tornado框架深度对比:从MVCMTV到高并发架构设计
  • 湖南畅想网站建设大连品牌网站建设公司
  • S4和ECC或者不通CLIENT,不通HANA服务器互相取数
  • Linux中控制台初始化console_init函数的实现
  • pycharm 默认终端设置 cmd
  • JavaScript 加密工具 sojson.v5 全解析:原理、应用与实践
  • 【Python库包】ESMF 库包介绍及安装
  • HarmonyOS ArkUI框架自定义弹窗选型与开发实战
  • 智能体开发(2)智能数据处理Agent
  • Visual Studio在一个解决方案管理多项目属性
  • 网站图片防盗连怎么做韶关营销网站开发
  • 10.17 设置组件导航和页面路由
  • 福田做商城网站建设找哪家公司比较安全简约好看的网站模板免费下载
  • 【GD32F527_EVAL】USB 驱动移植 和 USB CDC Device 接入PC实验
  • 网站开发网站定制查看网站源代码建站可以
  • stm32_QT6怎么打包
  • c 做网站流程如何做做网站
  • 深度剖析大模型Function Calling:从原理到优化策略
  • SQL入门:表关联-从基础到优化实战
  • YOLOv3 技术总结
  • 为什么有些前端开发者能快速交付,有些还在纠结架构设计