FastAPI同路径下实现ID(int)与分类(str)查询的方法求助
解决FastAPI同路径下整数ID与字符串分类查询的路由冲突问题
问题原因
FastAPI的路由匹配遵循定义顺序优先原则,你定义的两个路由/{item_id}和/{category}路径结构完全一致,当请求分类字符串时,会优先匹配第一个整数ID路由,尝试将字符串转换为整数失败,因此抛出类型错误。
方案一:合并路由逻辑,按参数类型分支处理
将两个查询逻辑合并到同一个路由函数中,先尝试将路径参数转换为整数(匹配ID查询),转换失败则按分类查询:
from fastapi import HTTPException, status from typing import Union from your_module import models, db, Item @router.get("/{param}", response_model=Union[Item, list[Item]], status_code=status.HTTP_200_OK) def get_item_or_category(param: str): # 尝试匹配ID查询 try: item_id = int(param) item = db.query(models.Item).filter(models.Item.id == item_id).first() if not item: raise HTTPException(status_code=404, detail="未找到该ID对应的物品") return item except ValueError: # 匹配分类查询 items = db.query(models.Item).filter(models.Item.category == param).all() if not items: raise HTTPException(status_code=404, detail="该分类下无物品") return items
方案二:使用路径参数的类型/正则约束,区分路由匹配规则
利用FastAPI支持的路径参数类型后缀或正则表达式,让框架能根据参数格式自动匹配对应的路由:
方法2.1:使用类型后缀限定
通过{item_id:int}和{category:str}明确参数类型,FastAPI会优先匹配纯数字路径到ID路由,其他路径到分类路由:
from fastapi import HTTPException, status from your_module import models, db, Item # 匹配纯数字的ID路由 @router.get("/{item_id:int}", response_model=Item, status_code=status.HTTP_200_OK) def get_item(item_id: int): item = db.query(models.Item).filter(models.Item.id == item_id).first() if not item: raise HTTPException(status_code=404, detail="未找到该ID对应的物品") return item # 匹配字符串的分类路由 @router.get("/{category:str}", response_model=list[Item], status_code=status.HTTP_200_OK) def get_items_by_category(category: str): items = db.query(models.Item).filter(models.Item.category == category).all() if not items: raise HTTPException(status_code=404, detail="该分类下无物品") return items
方法2.2:使用正则表达式更严谨约束
如果需要更精确的参数格式控制(比如分类不能是纯数字),可以用正则限定路径参数:
from fastapi import HTTPException, status from your_module import models, db, Item # 匹配纯整数的ID路由(正则限定只能是数字) @router.get("/{item_id:path(^\\d+$)}", response_model=Item, status_code=status.HTTP_200_OK) def get_item(item_id: int): item = db.query(models.Item).filter(models.Item.id == item_id).first() if not item: raise HTTPException(status_code=404, detail="未找到该ID对应的物品") return item # 匹配非纯数字的分类路由(正则限定以非数字开头) @router.get("/{category:path(^\\D.*$)}", response_model=list[Item], status_code=status.HTTP_200_OK) def get_items_by_category(category: str): items = db.query(models.Item).filter(models.Item.category == category).all() if not items: raise HTTPException(status_code=404, detail="该分类下无物品") return items
方案对比
- 方案一:代码紧凑,适合逻辑简单的场景,但Swagger文档中返回类型会显示为两种可能,不够直观。
- 方案二:路由分离,Swagger文档更清晰,每个路由的职责明确,更符合REST API的设计规范。
内容的提问来源于stack exchange,提问作者Hakan
相关产品推荐
相关产品推荐

