FastAPI简单使用

一、简介

FastAPI 是一个高性能 Web 框架,用于构建 API。

主要特性:

  • 快速:非常高的性能,与 NodeJS 和 Go 相当
  • 快速编码:将功能开发速度提高约 200% 至 300%
  • 更少的错误:减少约 40% 的人为错误
  • 直观:强大的编辑器支持,自动补全无处不在,调试时间更少
  • 简易:旨在易于使用和学习,减少阅读文档的时间。
  • 简短:减少代码重复。
  • 稳健:获取可用于生产环境的代码,具有自动交互式文档
  • 基于标准:基于并完全兼容 API 的开放标准 OpenAPI 和 JSON Schema 

 

先来看一下效果:

 编写的api接口,可以自动生成swagger文档,非常方便。

 

二、创建FastAPI项目

安装模块

要求 python3.6版本及以上

pip install fastapi
pip install uvicorn

 

创建项目

创建一个project_test空目录

目录结构如下:

.
├── application
│   └── user_manager
│       └── operation_user.py
├── main.py
├── models.py
└── settings.py

 

主程序文件

main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from application.user_manager.operation_user import user_manager_router

# from fastapi.requests import Request
from models import User
from tortoise.contrib.fastapi import register_tortoise
from settings import TORTOISE_ORM

app = FastAPI()
# 添加中间件来处理跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许任何域,或者指定特定域
    allow_credentials=True,
    allow_methods=["*"],  # 允许任何方法,或者指定特定方法
    allow_headers=["*"],  # 允许任何头,或者指定特定头
)

register_tortoise(
    app, config=TORTOISE_ORM
)

app.include_router(user_manager_router, prefix="/user", tags=["用户管理"])

if __name__ == '__main__':
    uvicorn.run('main:app', host="127.0.0.1", port=8000, reload=True)
View Code

 

数据结构文件

models.py

from enum import Enum
from tortoise.models import Model
from tortoise import fields

class User(Model):
    """
    用户表
    """
    id = fields.IntField(pk=True)
    username = fields.CharField(max_length=20, description="用户名")
    password = fields.CharField(max_length=256, description="密码")
    phone = fields.CharField(max_length=11, null=True, description="手机号")
    email = fields.CharField(max_length=255, null=True, description="邮箱")
    # 1-管理员,2-普通用户  
    role = fields.CharField(max_length=255, description="角色",
                            choices=[(1, 2)], default=2
                            )

    class Meta:
        table = "user"  # 数据库中的表名称
        table_description = '用户表'
View Code

 

配置文件

settings.py

TORTOISE_ORM = {
    'connections': {
        # 'default': {
        #     # 'engine': 'tortoise.backends.asyncpg',  PostgreSQL
        #     'engine': 'tortoise.backends.mysql',  # MySQL or Mariadb
        #     'credentials': {
        #         'host': 'localhost',
        #         'port': '3306',
        #         'user': 'root',
        #         'password': '123456',
        #         'database': 'test',
        #         'minsize': 1,
        #         'maxsize': 5,
        #         'charset': 'utf8',
        #         "echo": True
        #     }
        # },
        "default": "sqlite://db.sqlite3"
    },
    'apps': {
        'models': {
            'models': ['aerich.models', "models"],  # aerich.models迁移模型
            'default_connection': 'default',
        }
    },
    'use_tz': False,    'timezone': 'Asia/Shanghai'
}
View Code

 

用户视图文件

operation_user.py

"""
操作 User表
"""
import math

from fastapi import APIRouter, Depends, HTTPException, Form, status, Query
from typing import List
from datetime import datetime, timedelta, timezone
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer, SecurityScopes
from pydantic import BaseModel, Field
from starlette.responses import JSONResponse
from tortoise.expressions import Q
from tortoise import Tortoise, run_async
from models import User
from fastapi import HTTPException, status
from tortoise.exceptions import DoesNotExist
import bcrypt
# import pymysql
from settings import TORTOISE_ORM

user_manager_router = APIRouter()

async def get_user_info(user: User) -> dict:
    return {
        "user_id": user.id,
        "username": user.username,
        "password": user.password,
        "phone": user.phone,
        "email": user.email,
        "role": user.role,
    }


@user_manager_router.get("/", summary="获取所有用户")
async def get_users():
    user_infos = await User.all().values("id", "username", "password", "phone", "email","role")
    return user_infos


@user_manager_router.get("/user_pages", summary="用户列表分页")
async def user_list(
    page: int = Query(1, description="页码"),
    PageSize: int = Query(10, description="每页数量")
):
    user_info = await User.all().offset((page - 1) * PageSize).limit(PageSize)
    user_total = await User.all().count()

    return {"CurrentPage": page, "PageSize": PageSize, "TotalPage": user_total, "user_info": user_info}


@user_manager_router.get("/{user_id}", summary="根据ID获取单个用户详细信息")
async def get_user_detail(user_id: int):
    # 尝试根据用户名或账号查找用户
    users_detail = await User.filter(id=user_id).all()
    return users_detail


@user_manager_router.post("/user_role_pages", summary="用户角色查询")
async def user_role_pages(
    role: int = Query(2, description="角色:[参数说明:管理员(1),普通用户(2)]"),
    page: int = Query(1, description="页码"),
    PageSize: int = Query(10, description="每页数量")
):
    user_role_info = await User.filter(role=role).offset((page - 1) * PageSize).limit(PageSize)
    user_role_total = await User.filter(role=role).count()

    return {"CurrentPage": page, "PageSize": PageSize, "TotalPage": user_role_total, "user_role_info": user_role_info}


@user_manager_router.post('/', summary="添加用户")
async def add_user(
        username: str = Form(description="用户名", summary="用户名"),
        password: str = Form(description="密码"),
        phone: str = Form(description="联系电话", max_length=11),
        email: str = Form(description="邮箱"),
        role: int = Form(description="角色:[参数说明:管理员(1)普通用户(2)]", default=2),
):

    # 检查用户是否存在逻辑保持不变
    if await User.filter(username=username).exists():
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="用户已经存在")

    # 使用bcrypt对密码进行哈希处理
    hashed_pwd = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())

    # 使用哈希后的密码创建用户
    user = await User.create(username=username, password=hashed_pwd.decode('utf-8'),
                             # 存储为字符串
                             phone=phone, email=email,role=role)

    user_info = await get_user_info(user)
    return user_info


@user_manager_router.put("/{user_id}", summary="修改用户信息")
async def update_user(
        user_id: int,
        phone: str = Form(description="联系电话", max_length=11),
        email: str = Form(description="邮箱"),
        role: int = Form(description="默认角色为普通用户", default=2, choices=[1, 2]),
):
    upadte_user_id = await User.filter(id=user_id)
    if upadte_user_id:
        await User.filter(id=user_id).update(phone=phone, email=email,role=role)
        await User.filter(id=user_id)
        return JSONResponse(content={"status_code": 200, "msg": "用户数据更新成功"})
    else:
        return HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="更新失败")


@user_manager_router.put("/update_password/{user_id}", summary="修改用户密码")
async def update_user_password(
        user_id: int,
        password: str = Form(description="密码")
):
    update_user_id = await User.filter(id=user_id)
    if update_user_id:
        hashed_pwd = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
        await User.filter(id=user_id).update(password=hashed_pwd.decode('utf-8'))
        await User.filter(id=user_id)
        return JSONResponse(content={"status_code": 200, "msg": "用户密码更新成功"})
    else:
        return HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="用户密码更新失败")


@user_manager_router.delete("/{user_id}", summary="删除用户")
async def delete_user(user_id: int):
    await User.filter(id=user_id).delete()
    return {"message": "success"}


@user_manager_router.post("/login", summary="用户登录")
async def login(username: str = Form(description="用户名"),
                password: str = Form(description="密码")
                ):
    try:
        # 尝试根据用户名或账号查找用户
        user = await User.get(username=username)
    except DoesNotExist:
        # 如果用户不存在,则抛出异常
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户名或账号不存在")

    user_active = await User.filter(username=username)
    if not user_active:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="用户未激活")


    # 使用bcrypt检查密码是否匹配
    if bcrypt.checkpw(password.encode('utf-8'), user.password.encode('utf-8')):
        # 密码匹配,准备返回用户信息,但不包括密码
        user_info = await get_user_info(user)
        return JSONResponse(content={"status_code": 200, "msg": "登录成功", "user_info": user_info,})
    else:
        # 密码不匹配
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="密码错误")
View Code

 

这里使用的是sqlite3数据库,如果是mysql,修改对应的配置即可。

生成表结构,执行以下2个命令

aerich init -t settings.TORTOISE_ORM
aerich init-db

注意:第二个命令执行之后,会hold住,只要没有报错,就可以关闭窗口了。

 

运行项目

python main.py

执行输出:

INFO:     Will watch for changes in these directories: ['E:\\python_script\\FastAPI\\project_test']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [23092] using StatReload
INFO:     Started server process [21552]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

默认会监听8000端口,访问swagger页面

http://127.0.0.1:8000/docs

 

访问效果就是文章开头的图片,这里就不重复了。

 

三、swagger调用接口

点击添加用户接口

输入相关信息,点击Execute会调用接口

调用接口的相关信息,显示如下:

这里可以看到使用curl调用接口的完整命令,返回http状态为200,表示调用成功,接口返回表数据

 

调用登录接口

 提示登录成功

 

接口逻辑代码都在operation_user.py,里面有相关注释,相信应该看的懂。

 

posted @ 2025-01-07 16:09  肖祥  阅读(7)  评论(0编辑  收藏  举报