侧边栏壁纸
博主头像
ahao

A student who writes the code of Python and Java.

  • 累计撰写 15 篇文章
  • 累计创建 22 个标签
  • 累计收到 12 条评论

【FastAPI学习】(三)路径参数

ahao
2022-04-14 / 0 评论 / 2 点赞 / 101 阅读 / 4,915 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

路径参数

一、什么是路径参数

在介绍路径参数之前,我们需要了解URL,URI,Path的概念。URL(Uniform Resource Locator)是统一资源定位符的简称,通过URL就可以找到服务器上的特定资源,例如https://www.uxhao.com/queryTeacher/ahao。URI(Uniform Resource Identifier)是统一资源标识符的简称,URI描述的是服务端上服务软件上某项目地址,例如/queryTeacher/ahao。Path为路径,描述的是项目或者模块中资源的相对路径。例如/ahao

以下代码示例是一开始我们创建的简单接口,该接口是不需要任何参数的,只需要在浏览器中访问http://127.0.0.1:8000/ 即可显示返回信息。但是在日常开发中我们可能有这样的需求:前端向后台数据接口请求数据时需要提供特定的请求查询参数。而提供请求参数的方式有多种,如:路径参数、查询参数、请求体等方式。本节主要对路径参数进行详细介绍。

代码清单3-1
@app.get("/")  
def root():  
    return {"message": "Hello World"}  

首先,提出一个案例。实现一个讲师详细信息查询数据接口,该采用哪种方式呢?请思考这样一个问题,讲师可能有多个,如果针对每个讲师都单独实现一个数据接口用来查询他们的详细信息,这明显是不太合理的。一种合理的方式是设计一个通用的数据接口,只需要向该数据接口提供讲师的某个唯一身份标识,便能够从该数据接口查询到满足该唯一身份标识的讲师的详细信息。

针对讲师详细信息查询案例,我们可以通过路径参数传递讲师姓名(假设姓名不重复)数据接口中,这样FastAPI拿到路径参数中的讲师姓名就能返回对应的讲师详细信息到数据接口中。代码示例如代码清单3-2。

代码清单3-2
teachers = [
    {"id": 1001, "name": "ahao", "age": 24},
    {"id": 1002, "name": "腾哥", "age": 40},
    {"id": 1003, "name": "云哥", "age": 50},
]

@app.get("/queryTeacher/{teacherName}")
def queryTeacher(teacherName):
    for teacher in teachers:
        if teacher["name"] == teacherName:
            return teacher
    return {"该讲师不存在"}

首先我们定义了一个讲师列表teachers来模拟数据库表,每个讲师的详细信息记录对应一个字典类型,key-value表示讲师的各个属性以及属性值。查询讲师信息我们采用GET请求方式,然后定义了queryTeacher()函数和对应数据接口的URI以及路径参数teacherName(讲师姓名),并设置queryTeacher()函数的形参为teacherName,对于路径参数需要使用 { }进行标识。queryTeacher()函数的功能是接收路径参数teacherName,用该路径参数查询讲师列表list中name属性等于teacherName的那一条记录,找到后直接返回该条讲师详细信息记录,若未查询到则直接返回提示信息。查询讲师姓名为“ahao”的讲师的详细信息结果如下图所示。

image-20220411225458219

二、有类型的路径参数

在讲解有类型的路径参数之前,先看代码清单3-3,这里接收到的路径参数name的数据类型会是什么呢?我们可以请求该接口,假如路径参数为123,请求后的结果如图所示。从图中可以明显看出我们输入的路径参数123经过FastAPI返回后变成了字符串"123",而不是数值123。

代码清单3-3
@app.get("/{name}")
def pathValueType(name):
    return {"name": name}

由此可以得出结论,在FastAPI中,路径参数的数据类型默认为字符串类型。读者可能会这样的疑问,有时候我们希望传递数据类型为整型的路径参数。例如通过路径参数传递年龄age,在FastAPI中能实现吗?答案是肯定的,在FastAPI中可以使用标准的 Python 类型注释为函数中的路径参数声明类型,实际上是由 Pydantic 在幕后完成数据校验。如代码清单3-3, pathValueType()函数中为形参age声明了参数类型为int型。当我请求该接口时输入24,从返回结果中可以知道FastAPI在后台处理该路径参数的类型为int型。

代码清单3-3
@app.get("/{age}")
def pathValueType(age: int):
    return {"age": age}

image-20220411231606933

在之前的讲师详细信息查询接口案例中,实际情况下讲师姓名是可能重复的,常常根据讲师编号(id)作为讲师身份的唯一标识符,假设该讲师编号为int型,则改造后的代码如代码清单3-4。为路径参数讲师编号teacherId声明了数据类型为int型,查询讲师编号为1001的讲师详细信息结果如图所示。

代码清单3-4
teachers = [
    {"id": 1001, "name": "ahao", "age": 24},
    {"id": 1002, "name": "腾哥", "age": 40},
    {"id": 1003, "name": "云哥", "age": 50},
]

@app.get('/queryTeacher/{teacherId}')
def queryTeacher(teacherId: int):
    for teacher in teachers:
        if teacher["id"] == teacherId:
            return teacher
    return {"该讲师不存在"}
image-20220411192537126

根据讲师详细信息查询案例,我们大概了解了路径参数的概念及声明其数据类型。当我们传入的路径参数与声明的类型不一致会发生什么情况呢?例如在代码清单3-4实现的接口中输入路径参数ahao,会返回如下HTTP错误,msg提示路径参数不是一个有效的整型。

{
  "detail":[{
    "loc":["path","teacherId"],
    "msg":"value is not a valid integer",
    "type":"type_error.integer"
  }]
}
image-20220411224311729

三、路由顺序问题

在日常开发中我们可能会设计出以下情况的数据接口:

/getUser/{user_id}用来获取指定用户id的用户信息。

/getUser/current 用来获取当前用户的信息。

编写这两个数据接口的代码如代码清单3-5所示。启动服务器后访问 /getUser/current,应该是会出现 {"message": "query current user!"}的,但是从访问结果中看到,并不是返回/getUser/current 返回结果,而是返回了/getUser/{user_id}接口的返回结果。

代码清单3-5
# 路径顺序问题
@app.get("/getUser/{user_id}")
def getUserById(user_id: str):
    return {"message": "query user by user id!", "user_id": user_id}
  
@app.get("/getUser/current")
def getCurrentUser():
    return {"message": "query current user!"}
image-20220424234847113

为什么会出现这种情况呢?是因为FastAPI中存在路径参数顺序问题,当我们请求数据接口时,FastAPI按照路径顺序依次查找,/getUser/{user_id}/getUser/current之前定义,当我们访问/getUser/current时FastAPI匹配到了getUser/{user_id}, 把current识别成了参数。而不是一个具体的请求路径。

那我们要让这两个数据接口都生效该怎么做呢?其实很简单,我们只需要调整这两个接口定义的顺序即可,将/getUser/current放在/getUser/{user_id}之前即可。如代码清单3-6所示。

代码清单3-6
# 路径顺序问题
@app.get("/getUser/{user_id}")
def getUserById(user_id: str):
    return {"message": "query user by user id!", "user_id": user_id}
  
@app.get("/getUser/current")
def getCurrentUser():
    return {"message": "query current user!"}
image-20220424235152987 image-20220424235215824

从测试结果中看到,此时,两个数据接口均生效。所以我们在编写代码的时候,一定要注意,相同匹配的时候,默认只会按照顺序匹配。

四、预设路径参数

现在我们有这样一个需求,设计一个通过歌手名查询代表作的数据接口,限制了只能查询部分指定歌手,例如只能查【陈奕迅,林宥嘉,家驹】的代表作。此时Python自带的枚举类Enum便派上用场了。

只需要三个步骤即可实现,①导入 Enum 并创建一个继承自 strEnum 的子类。②通过从 str 继承,API 文档将能够知道这些值必须为 string 类型并且能够正确地展示出来。③创建具有固定值的类属性。实现如代码清单3-7所示。

代码清单3-7
# 使用枚举类型
class SingerName(str, Enum):
    Eason = "陈奕迅"
    LYJ = "林宥嘉"
    JiaJu = "家驹"

@app.get("/singer/{singer_name}")
async def get_model(singer_name: SingerName):
    if singer_name == SingerName.Eason:
        return {"author_name": singer_name, "message": "富士山下"}
    if singer_name == SingerName.LYJ:
        return {"author_name": singer_name, "message": "说谎"}
    else:
        return {"author_name": singer_name, "message": "Amani"}
image-20220425000756570

当我们提供的路径参数不是枚举类型中的值时,会提示不是枚举类型中的有效值。

image-20220425000910040
0

评论区