如何在FastAPI中实现多可选参数互斥(仅允许传其一)
实现互斥查询参数的几种替代方式
首先要指出你现有代码的一个隐患:它只处理了单个参数存在的情况,但没有校验多个参数同时传入的非法场景。比如用户同时传guid和code时,代码会直接返回guid对应的结果,这违反了“仅能传入一个参数”的要求。下面是几种更严谨的实现方式:
方案1:先统计非空参数数量做校验
先统计三个参数里非空的数量,确保只有一个参数被传入,再处理各自的逻辑:
from fastapi import status from fastapi.responses import PlainTextResponse from typing import Optional, Union, List from your_module import Locations, router @router.get("/api/locations") def get_functional_locations( guid: Optional[str] = None, code: Optional[str] = None, path: Optional[str] = None, ) -> Union[List[Locations], PlainTextResponse]: # 统计非空参数的数量 non_empty_count = sum(1 for param in [guid, code, path] if param is not None) if non_empty_count != 1: return PlainTextResponse( "必须且只能传入guid、code、path中的一个参数", status_code=status.HTTP_400_BAD_REQUEST ) if guid: return ... # 处理guid逻辑 elif path: return ... # 处理path逻辑 elif code: return ... # 处理code逻辑
方案2:用自定义依赖封装校验逻辑
把互斥参数的校验逻辑抽离成独立依赖,既保证代码整洁,也方便后续复用:
from fastapi import Depends, Query, HTTPException from fastapi import status from typing import Optional, Union, List, Tuple from your_module import Locations, router def get_location_query_param( guid: Optional[str] = Query(None), code: Optional[str] = Query(None), path: Optional[str] = Query(None), ) -> Tuple[str, str]: # 整理参数键值对,过滤空值 param_map = { "guid": guid, "code": code, "path": path } valid_params = [(key, value) for key, value in param_map.items() if value is not None] if len(valid_params) != 1: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="必须且只能传入guid、code、path中的一个参数" ) # 返回(参数类型, 参数值) return valid_params[0] @router.get("/api/locations") def get_functional_locations( param_info: Tuple[str, str] = Depends(get_location_query_param) ) -> Union[List[Locations], PlainTextResponse]: param_type, param_value = param_info if param_type == "guid": return ... # 处理guid逻辑 elif param_type == "path": return ... # 处理path逻辑 elif param_type == "code": return ... # 处理code逻辑
方案3:用Pydantic模型做校验
借助Pydantic的validator来实现互斥参数的校验,适合参数逻辑更复杂的场景:
from fastapi import Depends, HTTPException from fastapi import status from pydantic import BaseModel, validator from typing import Optional, Union, List from your_module import Locations, router class LocationQueryParams(BaseModel): guid: Optional[str] = None code: Optional[str] = None path: Optional[str] = None @validator("*", pre=True, always=True) def check_single_param(cls, v, values): # 统计非空参数数量 non_empty = sum(1 for val in values.values() if val is not None) # 如果当前参数非空且已经有其他非空参数,抛出错误 if v is not None and non_empty > 0: raise ValueError("只能传入guid、code、path中的一个参数") return v @router.get("/api/locations") def get_functional_locations( params: LocationQueryParams = Depends() ) -> Union[List[Locations], PlainTextResponse]: if params.guid: return ... # 处理guid逻辑 elif params.path: return ... # 处理path逻辑 elif params.code: return ... # 处理code逻辑 else: return PlainTextResponse( "必须传入guid、code、path中的一个参数", status_code=status.HTTP_400_BAD_REQUEST )
这几种方案都能严格保证“仅传入一个参数”的要求,避免了现有代码的漏洞。
内容的提问来源于stack exchange,提问作者kaiserm99
相关产品推荐
相关产品推荐

