FastAPI冲突路径参数在端点-好的做法?



我正在使用FastAPI为资源student创建2个GET方法。我希望通过两种方式获得student:通过student_idstudent_name

问题是,我最初创建了两个端点,如下所示

@app.get("/student/{student_name}", response_model=schemas.Student, status_code=200)
def get_student_by_name(student_name: str, db: Session = Depends(get_db)):
db_student = crud.get_student_by_name(db, student_name)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

@app.get("/student/{student_id}", response_model=schemas.Student, status_code=200)
def get_student_by_id(student_id: int, db: Session = Depends(get_db)):
db_student = crud.get_student_by_id(db, student_id)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

问题是端点名称相互冲突,/student后面跟着一个参数,只有一个可以工作-在这种情况下只有/student/{student_name},因为它是在前面定义的。因此,我想出了这个简单的解决方案,即在端点名称中添加更多内容:

@app.get("/student/{student_name}", response_model=schemas.Student, status_code=200)
def get_student_by_name(student_name: str, db: Session = Depends(get_db)):
db_student = crud.get_student_by_name(db, student_name)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

@app.get("/student/byid/{student_id}", response_model=schemas.Student, status_code=200)
def get_student_by_id(student_id: int, db: Session = Depends(get_db)):
db_student = crud.get_student_by_id(db, student_id)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

我将/byid添加到get_student)by_id方法的端点名称中。虽然两个端点现在都可以工作,但我想知道这是否被认为是一种良好的实践?当需要使用单个路径参数查询一个资源以区分端点名称时,最佳实践是什么?

遇到同样的问题已经好几个小时了,只是在试验的时候发现了一个完美的解决方案:

@app.get("/student/{student_name:str}", response_model=schemas.Student, status_code=200)
def get_student_by_name(student_name: str, db: Session = Depends(get_db)):
...
@app.get("/student/{student_id:int}", response_model=schemas.Student, status_code=200)
def get_student_by_id(student_id: int, db: Session = Depends(get_db)):
...

添加这些"转换器"到您的路径将根据指定的类型将您的请求路由到正确的端点:)

我能够在Starlette中找到这个文档,我不太熟悉,但是FastAPI是建立在它之上的。https://www.starlette.io/routing/

我要这样做

@app.get("/student/{student_id}", response_model=schemas.Student, status_code=200)
def get_student(student_id: str, db: Session = Depends(get_db)):
db_student = crud.get_student_by_id(db, student_id)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

# use search criterias as query params
@app.get("/student/", response_model=List[schemas.Student], status_code=200)
def get_students(student_name: string = None, db: Session = Depends(get_db)):
# Query inside your crud file
query = db.query(Student)
if student_name:
# if you want to search similar items
query = query.filter(Student.name.like(f"%{student_name}%"))
# if you want to search an exact match
query = query.filter(Student.name == student_name)

return query.all()

有了这个,你的代码将对未来的变化更开放一点,我将只使用url参数时,搜索id,任何其他搜索条件可以作为一个过滤器参数处理使用查询参数

最佳实践与否是主观的。

如果我想确保与web框架的兼容性,我移植到FastAPI,它显示了原始的行为,然后有一种方法,我可以去复制它。

@app.get("/student/{student_id}",
response_model=schemas.Student,
status_code=200)
def get_student_by_id(
student_id: int = Path(
title="The ID of the student to get"),
db: Session = Depends(get_db)):
db_student = crud.get_student_by_id(db, student_id)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student
...

@app.get("/student/{student_name}",
response_model=schemas.Student,
status_code=200)
def get_student_by_name(
student_name: str,
db: Session = Depends(get_db)):
db_student = crud.get_student_by_name(db, student_name)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/

由于FastAPI对类型和文档的强调,对我来说,相同的路径前缀应该智能地路由到更具体的int-convertable,特别是当更具体的出现在前面时,这是有意义的。

如果我可以放弃更强的文档和验证,我可以像这样涵盖这两个基础:

@app.get("/student/{student_name_or_id}",
response_model=schemas.Student,
status_code=200)
async def get_student(
student_name_or_id: str = Path(
title="The ID or name of the student to get"),
db: Session = Depends(get_db)):
if student_name_or_id.isdigit():
db_student = crud.get_student_by_id(db, int(student_name_or_id))
else:
db_student = crud.get_student_by_name(db, student_name_or_id)
if db_student is None:
raise HTTPException(status_code=404, detail="Student not found")
return db_student

https://fastapi.tiangolo.com/tutorial/path-params/path-convertor

用于验证参数是否为int或学生名的现有逻辑,然后验证它们或返回适当的错误。我看到你已经有了这个逻辑,所以这不是最大的缺点。

我最初经常使用这种方法。了解了包含的OpenAPI文档的价值后,我更喜欢单独的函数。有时最好保持你的网站链接。例如,WordPress可以在/2022/08/python/faster-fastapi同时提供页面,一个使用日期,另一个使用类别和段符。

最新更新