FastAPI从入门到实战(10)——响应模型与状态码

向右看齐 2024-03-31 13:18 230阅读 0赞

前面一直记录的是请求相关的内容,这篇文章开始记录一下响应相关的内容,包括请求模型和模型继承以及状态码等相关的内容。

一个任意dict构成的基本响应

  1. # 任意dict构成的响应
  2. @app06.get("/stu06/dict", response_model=Dict[str, float])
  3. async def stu06_read_keyword_weights():
  4. return {
  5. "foo": 2.3, "bar": 3.4}

上面是用Dict声明的一个响应模型,只需要声明键和值的类型,就能直接响应。

image-20221129181122307

定义基本响应模型

  1. # 定义一个基本模型类
  2. class userIn(BaseModel):
  3. username: str
  4. password: str
  5. email: EmailStr
  6. sex: str
  7. age: int
  8. # 响应模型:输出与输入相同的模型数据
  9. @app06.post("/stu06/user_response", response_model=userIn)
  10. def stu06_creat_user(user: userIn):
  11. return user

这里利用Pydantic定义一个基本的模型类,包含username、password等字段,然后声明一个post请求,请求参数默认Query类型,服务器接受到请求后将接收的数据再次以userIn的类型进行返回。

image-20221129224540214

定义输出响应模型

  1. # 添加一个输出模型类
  2. class userOut(BaseModel):
  3. username: str
  4. email: EmailStr
  5. sex: Optional[str] = "girl"
  6. age: Optional[int] = None
  7. # 响应输出模型
  8. @app06.post("/stu06/userout", response_model=userOut)
  9. def stu06_response_userout(user: userIn):
  10. return user

上面的例子不太能反映响应模型类的作用,为了更清楚的展示,这里重新定义一个类,类的名称叫做userOut,相较于上面的例子,少了一个password参数,然后同样是一个post请求,服务器接受一个userIn类型的参数,然后将接收的数据以userOut类型进行返回,响应结果就没有了password字段。

image-20221129224655033

为模型设置值

  1. userItem = {
  2. "name1": {
  3. "username": "name1", "password": "12312312", "email": "hidbiuwu@nenu.edu.cn"},
  4. "name2": {
  5. "username": "name2", "password": "12312312", "email": "hidddwu@163.com", "sex": "girl", "age": 25},
  6. "MinChess": {
  7. "username": "MinChess", "password": "12312312", "email": "MinChess@jiumoz.com", "sex": None,
  8. "age": 22}
  9. }
  10. # 响应详细参数
  11. @app06.get("/stu06/response_item", response_model=userOut)
  12. def stu06_response_item(
  13. username: str = Query(...)
  14. ):
  15. return userItem[username]

通常web开发中,响应的数据都是从数据库进行请求的,比如说用户登录,请求的时候只会发送用户名和密码,返回就需要返回之前用户注册的时候填的更多的基本信息,例如姓名、性别、年龄等;

这里我们首先定义一个字典userItem,字典的keyusername的值,value为一个字典;然后定义一个get请求,请求参数为username;前端发送一个带有username的请求,服务器接收请求后,在userItem中进行匹配,匹配到对应的字段后以userOut类型的形式进行返回。

image-20221129224833928

响应字段控制

忽略未设置的参数 response_model_exclude_nset

  1. # 响应未经参数设置的参数 response_model_exclude_nset=True
  2. @app06.get("/stu06/response_exclude_unset", response_model=userOut, response_model_exclude_unset=True)
  3. def stu06_response_item_exclude_unset(
  4. username: str = Query("name1")
  5. ):
  6. return userItem[username]

如上一个例子,当usernamename1的时候,返回是字段是userOut内所有的字段,但是名为name1的字典内,并没有age、sex的字段;遇到这种情况,就可以通过设置参数response_model_exclude_nset=True来进行控制;

image-20221129224849330

响应忽略默认值response_model_exclude_defaults

  1. # 响应忽略和默认值一样的字段 response_model_exclude_defaults=True
  2. @app06.get("/stu06/response_exclude_defaults", response_model=userOut, response_model_exclude_defaults=True)
  3. def stu06_response_item_exclude_defaults(
  4. username: str = Query("name2")
  5. ):
  6. return userItem[username]

同样,我们拿到数据发现和默认数据是相同的时候,需要自动忽略,就可以通过设置参数response_model_exclude_defaults=True来实现,即我们发现uesrItem内,名为name2的字典内sex字段和定义的userOut响应模型类的默认值是一样的,我们就进行忽略;

当然实际情况肯定不会是默认一个性别吭…

image-20221129224905361

响应忽略None字段response_model_exclude_none

  1. # 响应忽略None字段 response_model_exclude_none=True
  2. @app06.get("/stu06/response_exclude_none", response_model=userOut, response_model_exclude_none=True)
  3. def stu06_response_item_exclude_none(
  4. username: str = Query("MinChess")
  5. ):
  6. return userItem[username]

同理,我们需要忽略空字段的时候,就可以通过设置response_model_exclude_none=True参数来实现了,实际应用就是数据库中,某些字段为空会影响用户的体验,那么就可以直接这样设置来过滤空字段;

上面的例子就是,名为MinChess的字段内,sex字段为None,那么我们就不进行返回。

image-20221129224930523

响应只包含指定字段 response_model_include

  1. # 响应只包含指定字段 response_model_include
  2. @app06.get("/stu06/response_model_include", response_model=userOut, response_model_include=["username","age"])
  3. def stu06_response_model_include(
  4. username:str = Query("MinChess")
  5. ):
  6. return userItem[username]

我们定义的模型类可能不适用于所有场景,有些页面只需要头像或者姓名,但是重新定义一个类在参数很多的情况下就完全没必要,所以就可以手动进行指定,如上面的例子response_model_include=["username","age"],指定返回usernameage字段。

image-20221129224945565

响应排除指定字段 response_model_exclude

  1. # 响应排除指定字段 response_model_exclude
  2. @app06.get("/stu06/response_model_exclude", response_model=userOut,response_model_exclude=["email"])
  3. def stu06_response_model_exclude(
  4. username:str = Query("MinChess")
  5. ):
  6. return userItem[username]

弄清楚了上面那个,这个就很容易了,就是忽略某些字段嘛,这里就是忽略email字段。

image-20221129225006030

模型列表

  1. # 模型列表
  2. @app06.get("/stu06/response_users",response_model=List[userOut], response_model_exclude_none=True)
  3. def sru06_response_users(
  4. username1: Optional[str] = Query("name1"),
  5. username2: Optional[str] = Query("name2")
  6. ):
  7. return [userItem[username1],userItem[username2]]

模型列表就是响应的内容是一个列表,列表是我们定义的某个模型类类别的,比如管理系统,返回的就是很多个相同类的数据,但是实际应用肯定不是像这里这样传这么多username哈!

这里就直接设置response_modelList类型的就行,其中List为userOut类的;

image-20221129225036700

模型继承

  1. # 基本类
  2. class loginbase(BaseModel):
  3. phone_number:str
  4. name:str
  5. # 登录时用到的类,在此基础上,需要增加密码和验证码的字段
  6. class login(loginbase):
  7. password:str
  8. code:str
  9. # 登录成功后返回给前端的类,返回的字段和基本类相同,不需要增加或删除,直接pass
  10. class loginsuccess(loginbase):
  11. pass
  12. # 存储至数据库时的类,在基本模型基础上要添加一个经过处理的密码
  13. class logindb(loginbase):
  14. hash_password:str
  15. # 伪密码处理函数
  16. def password_hash(password:str):
  17. return "hash——"+password
  18. # 伪入库类:接收前端传来的login类
  19. def login_database(loginparam:login):
  20. # 将接收到的login类型下loginparam中的password字段进行处理(加密)
  21. hashpassword = password_hash(loginparam.password)
  22. # 首先通过Pydantic模型的.dict()方法将loginparam处理为拥有模型数据的dict
  23. # 再通过**将其中的参数传递到logindb中,python对其进行解包,便能一一对应进行直接传递了
  24. # 同时对hash_password进行另外关键字参数的设置
  25. login_db = logindb(**loginparam.dict(),hash_password=hashpassword)
  26. print("入库成功!",login_db.dict())
  27. # 返回logindb类型的数据
  28. return login_db
  29. @app06.post("/stu06/login",response_model=loginsuccess)
  30. async def stu06_login(login_param:login):
  31. # 返回请求成功的类型的数据
  32. loginsuc = login_database(login_param)
  33. return loginsuc

这个部分其实不主要记录关于响应相关的内容,主要是记录一种思想,实际应用中就是对于同一个user表,登录、响应、存储所涉及的字段都是不同的,所以可以通过定义基本的类,然后进行继承来实现扩展。这样可以减少大量的重复代码;

具体的一些知识点和操作参看上面的注释,写的很明白!

image-20221129225408766

状态码设置

HTTP状态码

HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字有分类的作用。不同的状态码代表不同的含义。

HTTP状态码分类

共分为5种类型:






























分类 分类描述
1xx 信息,服务器收到请求,需要请求者继续执行操作
2xx 成功,操作被成功接收并处理
3xx 重定向,需要进一步的操作以完成请求
4xx 客户端错误,请求包含语法错误或无法完成请求
5xx 服务器错误,服务器在处理请求的过程中发生了错误

常见状态码&含义

  1. 200 - 请求成功,已经正常处理完毕
  2. 301 - 请求永久重定向,转移到其它URL
  3. 302 - 请求临时重定向
  4. 304 - 请求被重定向到客户端本地缓存
  5. 400 - 客户端请求存在语法错误
  6. 401 - 客户端请求没有经过授权
  7. 403 - 客户端的请求被服务器拒绝,一般为客户端没有访问权限
  8. 404 - 客户端请求的URL在服务端不存在
  9. 500 - 服务端永久错误

直接利用数字声明

  1. @app06.get("/stu06/statuscode",status_code=200)
  2. async def stu06_status():
  3. return {
  4. "status-code":200}

上面的代码,就是直接在请求声明中定义了响应状态码为200;

image-20221129180157668

利用FastAPI进行设置

  1. @app06.get("/stu06/statuscode_fastapi",status_code=status.HTTP_200_OK)
  2. def stu06_fastcode():
  3. return {
  4. "status-code":200}

这里就是利用fastapi.status进行设置的便捷变量

image-20221129180401422

源码

  1. # -*- coding: utf-8 -*-
  2. # @Time: 2022/11/29 11:17
  3. # @Author: MinChess
  4. # @File: stu06.py
  5. # @Software: PyCharm
  6. from fastapi import APIRouter, Query ,status
  7. from pydantic import BaseModel, EmailStr, Field
  8. from typing import Optional,List,Dict
  9. app06 = APIRouter()
  10. # 任意dict构成的响应
  11. @app06.get("/stu06/dict", response_model=Dict[str, float])
  12. async def stu06_read_keyword_weights():
  13. return {
  14. "foo": 2.3, "bar": 3.4}
  15. # 定义一个基本模型类
  16. class userIn(BaseModel):
  17. username: str
  18. password: str
  19. email: EmailStr
  20. sex: str
  21. age: int
  22. # 响应模型:输出与输入相同的模型数据
  23. @app06.post("/stu06/user_response", response_model=userIn)
  24. def stu06_creat_user(user: userIn):
  25. return user
  26. # 添加一个输出模型类
  27. class userOut(BaseModel):
  28. username: str
  29. email: EmailStr
  30. sex: Optional[str] = "girl"
  31. age: Optional[int] = None
  32. # 响应输出模型
  33. @app06.post("/stu06/userout", response_model=userOut)
  34. def stu06_response_userout(user: userIn):
  35. return user
  36. userItem = {
  37. "name1": {
  38. "username": "name1", "password": "12312312", "email": "hidbiuwu@nenu.edu.cn"},
  39. "name2": {
  40. "username": "name2", "password": "12312312", "email": "hidddwu@163.com", "sex": "girl", "age": 25},
  41. "MinChess": {
  42. "username": "MinChess", "password": "12312312", "email": "MinChess@jiumoz.com", "sex": None,
  43. "age": 22}
  44. }
  45. # 响应详细参数
  46. @app06.get("/stu06/response_item", response_model=userOut)
  47. def stu06_response_item(
  48. username: str = Query(...)
  49. ):
  50. return userItem[username]
  51. # 响应未经参数设置的参数 response_model_exclude_nset=True
  52. @app06.get("/stu06/response_exclude_unset", response_model=userOut, response_model_exclude_unset=True)
  53. def stu06_response_item_exclude_unset(
  54. username: str = Query("name1")
  55. ):
  56. return userItem[username]
  57. # 响应忽略和默认值一样的字段 response_model_exclude_defaults=True
  58. @app06.get("/stu06/response_exclude_defaults", response_model=userOut, response_model_exclude_defaults=True)
  59. def stu06_response_item_exclude_defaults(
  60. username: str = Query("name2")
  61. ):
  62. return userItem[username]
  63. # 响应忽略None字段 response_model_exclude_none=True
  64. @app06.get("/stu06/response_exclude_none", response_model=userOut, response_model_exclude_none=True)
  65. def stu06_response_item_exclude_none(
  66. username: str = Query("MinChess")
  67. ):
  68. return userItem[username]
  69. # 响应只包含指定字段 response_model_include
  70. @app06.get("/stu06/response_model_include", response_model=userOut, response_model_include=["username","age"])
  71. def stu06_response_model_include(
  72. username:str = Query("MinChess")
  73. ):
  74. return userItem[username]
  75. # 响应排除指定字段 response_model_exclude
  76. @app06.get("/stu06/response_model_exclude", response_model=userOut,response_model_exclude=["email"])
  77. def stu06_response_model_exclude(
  78. username:str = Query("MinChess")
  79. ):
  80. return userItem[username]
  81. # 模型列表
  82. @app06.get("/stu06/response_users",response_model=List[userOut], response_model_exclude_none=True)
  83. def sru06_response_users(
  84. username1: Optional[str] = Query("name1"),
  85. username2: Optional[str] = Query("name2")
  86. ):
  87. return [userItem[username1],userItem[username2]]
  88. # 基本类
  89. class loginbase(BaseModel):
  90. phone_number:str
  91. name:str
  92. # 登录时用到的类,在此基础上,需要增加密码和验证码的字段
  93. class login(loginbase):
  94. password:str
  95. code:str
  96. # 登录成功后返回给前端的类,返回的字段和基本类相同,不需要增加或删除,直接pass
  97. class loginsuccess(loginbase):
  98. pass
  99. # 存储至数据库时的类,在基本模型基础上要添加一个经过处理的密码
  100. class logindb(loginbase):
  101. hash_password:str
  102. # 伪密码处理函数
  103. def password_hash(password:str):
  104. return "hash——"+password
  105. # 伪入库类:接收前端传来的login类
  106. def login_database(loginparam:login):
  107. # 将接收到的login类型下loginparam中的password字段进行处理(加密)
  108. hashpassword = password_hash(loginparam.password)
  109. # 首先通过Pydantic模型的.dict()方法将loginparam处理为拥有模型数据的dict
  110. # 再通过**将其中的参数传递到logindb中,python对其进行解包,便能一一对应进行直接传递了
  111. # 同时对hash_password进行另外关键字参数的设置
  112. login_db = logindb(**loginparam.dict(),hash_password=hashpassword)
  113. print("入库成功!",login_db.dict())
  114. # 返回logindb类型的数据
  115. return login_db
  116. @app06.post("/stu06/login",response_model=loginsuccess)
  117. async def stu06_login(login_param:login):
  118. # 返回请求成功的类型的数据
  119. loginsuc = login_database(login_param)
  120. return loginsuc
  121. # 直接修改
  122. @app06.get("/stu06/statuscode",status_code=200)
  123. async def stu06_status():
  124. return {
  125. "status-code":200}
  126. # 通过fastapi设置
  127. @app06.get("/stu06/statuscode_fastapi",status_code=status.HTTP_200_OK)
  128. def stu06_fastcode():
  129. return {
  130. "status-code":200}

感谢阅读!

博客链接:FastAPI从入门到实战(10)——响应模型与状态码

30b7b3ba49344b6691969078c7df196e.png

发表评论

表情:
评论列表 (有 0 条评论,230人围观)

还没有评论,来说两句吧...

相关阅读