Python|FastAPI的路由介绍及使用
本文将介绍如何使用 Router 路由处理 FastAPI 中的请求。同时以我自己开发系统的后端为例进行FastAPI使用的说明。
什么是路由
路由 Router 就像是一个流水线上的线长,协调生产,下达命令给不同的组长进行分工,然后执行基本的任务。路由器的工作目的是,在团队中工作时,您可能必须在团队成员(这里的团队负责人是队长)之间分配复杂性,这将有助于更快地完成项目,正确的 SME 将在该分支/路由器上工作.
路由是构建网络应用的一个重要部分。FastAPI 中的路由是灵活和方便的。路由是处理从客户端发送到服务器的 HTTP 请求的过程。HTTP 请求被发送到定义的路由,这些路由有定义的处理程序来处理请求和响应。这些处理程序被称为 Route Handler。
FastAPI 中的路由
参考 FastAPI 文档对路由器的介绍:如果你正在构建一个应用程序或一个 Web API,你很少会把所有东西都放在一个文件中。 FastAPI 提供了一个方便的工具来构建您的应用程序,同时保持所有的灵活性。
先来看一个例子:
from fastapi import FastAPI
app = FastAPI()
# 设置一个首页
@app.get('/')
async def welcome() -> dict:
return {"message": "Welcome to my Page"}
uvicorn 工具指向 FastAPI 的实例,为应用程序服务:
uvicorn main:app --port 8000 --reload
访问
INFO: Will watch for changes in these directories: ['D:\\WH_Flower\\backend']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [23056] using StatReload
INFO: Started server process [10788]
INFO: Waiting for application startup.
INFO: Application startup complete.
FastAPI()
实例可用于路由操作,正如前面所见。然而,这种方法通常用于在路由过程中只能处理单一路径的应用程序。在使用 FastAPI()
实例创建一个执行独特功能的单独路由的情况下,应用程序将无法运行两个路由,因为 uvicorn
工具只能运行一个入口点。
如果有多个路由
让我们了解一下如何用代码来创建路由器,下面是我们的基本(非路由器)代码,在这里,我创建了一个例子:主页、添加和删除用户的页面。由于这是一个例子,我只取了两个父路径为 '/user/'
的函数,但在现实生活中,你可能会发现 20-30 个这样的函数,所以需要创建路由器,因为在一个文件中处理太多复杂的东西会变得很麻烦。
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def welcome() -> dict:
return { "message": "Welcome to my Page"}
@app.get('/user/create_user')
def add_numbers():
return { "message": "Add a user!"}
@app.get('/user/delete_user')
def add_strings():
return { "message": "Delete a user!"}
那么,问题来了,我们如何处理需要一系列路由执行不同功能的广泛应用程序呢?答案是 APIRouter 类。
利用 APIRouter 类实现路由
APIRouter 类属于 FastAPI 包,为多个路由创建路径操作。APIRouter 类鼓励应用程序路由和逻辑的模块化和组织化。
APIRouter 类从 fastapi 包中导入,并创建一个实例。路由方法被创建并从创建的实例中分发,我这里以用户的基本功能为例,如下:
from fastapi import APIRouter
# create router
router = APIRouter(
prefix='/user',
tags = ['用户相关']
)
上面的代码将创建一个路由器实例,它可以带有一些参数,比如下面两个的含义:
- prefix:在特定页面中 fastapi 提供的每个装饰器中添加前缀
- tags:这将帮助我们找到属于哪个类别的功能,同时这会显示在FastAPI自动生成的文档中(类似于相关文章的主题标签)
注意,后方初学者可能会觉得难度飙升,但是没关系,我们这里只关心路由的情况,具体的API的内容不用管。
然后可以利用 APIRouter 类创建一个新的路径操作,创建一个新的python包apis
,然后在这个包里新建一个文件夹v1,代表我们各类API的版本号(这里建不建都是可以的,看你个人),这里展示一下用户相关的API:
# -*- coding: utf-8 -*-
"""
PROJECT_NAME: backend
FILE_NAME: user
AUTHOR: welt
E_MAIL: tjlwelt@foxmail.com
DATE: 2023/5/11
"""
from typing import List
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from curd import user_curd
from schemas import user_schema
from utils.db_connect import get_db
userRouter = APIRouter(tags=['用户相关'])
@userRouter.post("/register/", response_model=user_schema.User, summary="用户注册")
def create_user(user: user_schema.UserCreate, db: Session = Depends(get_db)):
"""
创建用户
"""
db_user = user_curd.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return user_curd.create_user(db=db, user=user)
@userRouter.get("/users/", response_model=List[user_schema.User], summary="获取全部的用户列表")
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
"""
获取全部用户
"""
users = user_curd.get_users(db, skip=skip, limit=limit)
return users
@userRouter.get("/users/{user_id}", response_model=user_schema.User, summary="根据用户ID来获取用户")
def read_user(user_id: int, db: Session = Depends(get_db)):
"""
详情页用到
"""
db_user = user_curd.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found!")
return db_user
@userRouter.post("/users/{user_id}", summary="根据用户ID删除用户")
def delete_user(user_id: int, db: Session = Depends(get_db)):
"""
根据用户ID删除用户(表格中管理用户时使用)
"""
return user_curd.delete_user(db=db, user_id=user_id)
@userRouter.post("/update_user_info/{user_id}", summary="更新用户信息")
async def update_user(user_id: int, user: user_schema.UserCreate, db: Session = Depends(get_db)):
"""
更新用户信息页面API
"""
db_user = user_curd.get_user_by_email(db, email=user.email)
if db_user:
if db_user.id == user_id:
return user_curd.update_user(db=db, user_id=user_id, user=user)
else:
raise HTTPException(status_code=400, detail="Email already registered!")
return user_curd.update_user(db=db, user_id=user_id, user=user)
@userRouter.post("/update_user_avatar/", summary="更新用户头像")
async def update_user(user: user_schema.UserChangeAvatar, db: Session = Depends(get_db)):
"""
更新用户信息页面API
"""
return user_curd.update_user_avatar(db=db, user=user)
@userRouter.get("/login/", summary="用户登录")
async def user_login(email: str, password: str, db: Session = Depends(get_db)):
"""
用户登录API
"""
db_user = user_curd.get_user_by_email(db, email)
hash_password = password + 'notreallyhashed'
if db_user is None:
raise HTTPException(status_code=404, detail="User not found!")
if hash_password == db_user.hashed_password:
return db_user
else:
return {"code": 501, "message": "密码错误!"}
@userRouter.post("/delete_user/{user_id}", summary="删除用户")
async def delete_point(user_id: int, db: Session = Depends(get_db)):
"""
删除用户
"""
return user_curd.delete_user(db=db, user_id=user_id)
因为我还需要地图点相关和详情页相关的API,评论摆烂了不想做,所以最终的文件目录结构如下:
将APIRouter 添加到FastAPI实例
APIRouter 类的工作方式与 FastAPI 类的工作方式相同。然而, uvicorn 不能使用 APIRouter 实例为应用程序服务,这与FastAPI 不同。使用 APIRouter 类定义的路由需要被添加到 FastAPI 实例中,以实现它们的功能。
为了使刚刚定义的路由可见,我们将使用 include_router()
方法把 add_router
路径操作处理程序到主 FastAPI 实例中,如下:
from fastapi import FastAPI
app = FastAPI()
# 设置一个首页
@app.get('/')
async def welcome() -> dict:
return {"message": "Welcome to my Page"}
# 添加FastAPI的API路由
app.include_router(User.userRouter)
app.include_router(MapPoints.pointRouter)
app.include_router(Abstract.abstractRouter)
include_router(router, ...)
方法负责在主程序的实例中加入用 APIRouter 类定义的路由添加到主应用程序的实例中,以使路由变得可见。
测试 Router 功能
启动 uvicorn 服务:
uvicorn src.main:app --reload --port 8000
在控制台看到如下信息,表示服务启动成功:
INFO: Will watch for changes in these directories: ['D:\\WH_Flower\\backend']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [23056] using StatReload
INFO: Started server process [10788]
INFO: Waiting for application startup.
INFO: Application startup complete.
浏览器如下,访问 http://127.0.0.1:8000/
:
最后,通过访问 http://127.0.0.1:8888/docs
来查看我们刚刚定义的接口,我们将看到自动 API 文档,包括来自所有子模块的路径,使用正确的路径(和前缀)和正确的标签名:
点击try it out!可以测试你的API,当然这里我是已经写了相关内容的: