一、引言:为什么选择FastAPI与SQLite?
在当今快速发展的互联网环境中,构建高效、安全且易于维护的API是每个开发者的必备技能。FastAPI 作为现代Python Web框架的佼佼者,凭借其异步支持 、自动生成Swagger文档 和强大的数据验证 能力,已成为API开发的首选。而SQLite作为轻量级嵌入式数据库,无需独立服务进程,单文件存储的特性使其成为快速原型开发和中小型项目的理想选择。
1.1 技术组合优势
- 开发效率:FastAPI的自动交互文档(Swagger UI)让API调试直观高效。
- 性能卓越:异步请求处理支持高并发,SQLite在本地操作中性能优异。
- 安全可靠:集成OAuth2/JWT认证机制,保障API安全性。
- 零配置部署:SQLite数据库无需复杂配置,适合快速迭代。
二、SQLite基础操作:从入门到精通
2.1 SQLite简介与安装
SQLite是一个基于C语言的关系型数据库引擎,其核心特点包括:
- 无服务器架构 :数据库以单个文件形式存储(如
mydb.db
)。 - ACID事务支持:确保数据操作的原子性、一致性、隔离性和持久性。
- 跨平台兼容:支持Windows、Linux、macOS等操作系统。
安装验证
Python内置sqlite3
模块,无需额外安装:
python
import sqlite3
print(sqlite3.sqlite_version) # 输出SQLite版本号
2.2 基础CRUD操作
Python操作sqlite的基本步骤:
1.导入 sqlite3 模块:import sqlite3
2.连接到 SQLite 数据库:sqlite3.connect('数据库名称.db')
3.创建一个游标对象:conn.cursor()
4.执行 SQL 语句:cursor.execute(sql语句)
5.SQL的操作:增删改查----重点
5.1 插入数据:INSERT INTO 表名称 (字段名称) VALUES (值)
5.2 查询数据:SELECT 字段名称 FROM 表名
5.3 删除数据:DELETE FROM 表名 WHERE 条件
5.4 修改数据:UPDATE 表名 SET 字段名称 = 值 WHERE 条件
6.提交事务:conn.commit()
7.关闭游标和连接:cursor.close(),conn.close()
python
# Python操作sqlite的基本步骤:
# 1.导入 sqlite3 模块:
import sqlite3
# 2.连接到 SQLite 数据库:
conn = sqlite3.connect('students.db')
# 3.创建一个游标对象
cur = conn.cursor()
# 4.执行 SQL 语句(创建表):
# 亲和类型:
# 整形:INTEGER
# ⽂本型:TEXT
# 浮点型:REAL
# 日期型:DATETIME
cur.execute('''
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
grade TEXT
);
''')
# 5.SQL的操作:增删改查----重点
# 5.1 插入数据:
# insert into 表名(字段1,字段2,字段3)values (?,?,?),("小明",20,100)
cur.execute('INSERT INTO students (name, age, grade) VALUES (?, ?, ?)', ('Alice', 20, 'A'))
cur.execute('INSERT INTO students (name, age, grade) VALUES (?, ?, ?)', ('Bob', 22, 'B'))
cur.execute('INSERT INTO students (name, age, grade) VALUES (?, ?, ?)', ('Charlie', 23, 'C'))
# 5.2 查询数据:
# 1)查询所有数据fetchall():
cur.execute("select * from students")
res = cur.fetchall() # 获取查询结果, 返回⼀个包含元组的列表
for row in res:
print(row)
# 2)查询单条数据fetchone():
cur.execute("select * from students where name = ?",("Bob",))
res1 = cur.fetchone()
print(res1)
# 5.3 删除数据
delete = cur.execute("delete from students where id = ?",(2,))
print(delete) # 返回的是一个游标对象
# 5.4 修改数据
cur.execute("update students set grade = ? where id = ?",("A+", 3))
# 6.提交事务:
conn.commit()
# 7.关闭游标和连接:
cur.close()
conn.close()
基础操作的学习网站:SQLite 教程 | 菜鸟教程
2.3 代码封装:函数版与面向对象版
函数版本:
python
import sqlite3
def create_connection(db_file):
"""创建数据库连接"""
conn = None
try:
conn = sqlite3.connect(db_file)
print(f"✅ 成功连接到数据库:{db_file}")
return conn
except sqlite3.Error as e:
print(f"❌ 连接失败:{e}")
return conn
def create_table(conn):
"""创建用户表"""
try:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
age INTEGER
)
''')
print("✅ 表创建成功")
except sqlite3.Error as e:
print(f"❌ 创建表失败:{e}")
def insert_user(conn, data):
"""插入用户数据"""
try:
cursor = conn.cursor()
# 检查用户是否存在
cursor.execute("SELECT id FROM users WHERE name=?", (data[0],))
user_id = cursor.fetchone()
if user_id is None:
cursor.execute("INSERT INTO users(name, age) VALUES(?,?)", data)
conn.commit()
print(f"✅ 用户 {data[0]} 插入成功")
else:
print(f"⚠️ 用户 {data[0]} 已存在(ID:{user_id[0]})")
except sqlite3.Error as e:
print(f"❌ 插入失败:{e}")
def update_user(conn, data):
"""更新用户数据"""
try:
cursor = conn.cursor()
cursor.execute("UPDATE users SET age=? WHERE name=?", data)
conn.commit()
print(f"✅ 用户 {data[1]} 年龄更新为 {data[0]}")
except sqlite3.Error as e:
print(f"❌ 更新失败:{e}")
def delete_user(conn, name):
"""删除用户数据"""
try:
cursor = conn.cursor()
cursor.execute("DELETE FROM users WHERE name=?", (name,))
conn.commit()
print(f"✅ 用户 {name} 删除成功")
except sqlite3.Error as e:
print(f"❌ 删除失败:{e}")
def select_all_users(conn):
"""查询所有用户"""
try:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
print("\n📋 当前用户列表:")
for row in rows:
print(f"ID: {row[0]}, 姓名: {row[1]}, 年龄: {row[2]}")
except sqlite3.Error as e:
print(f"❌ 查询失败:{e}")
def main():
database = "students2.db"
conn = create_connection(database)
if conn is not None:
create_table(conn)
try:
# 插入初始数据
insert_user(conn, ('Alice', 30))
insert_user(conn, ('Bob', 25))
# 更新数据
update_user(conn, (35, 'Alice'))
# 查询所有用户
select_all_users(conn)
# 删除用户
delete_user(conn, 'Bob')
# 再次查询
select_all_users(conn)
except sqlite3.Error as e:
print(f"❌ 操作异常:{e}")
finally:
conn.close()
print("🔌 数据库连接已关闭")
else:
print("❌ 无法创建数据库连接")
if __name__ == '__main__':
main()
面向对象版本:
python
import sqlite3
import logging
# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class SQLiteManager:
def __init__(self, db_name):
self.conn = sqlite3.connect(db_name)
self.cursor = self.conn.cursor()
# 1. 数据库数据的查询方法 -- 查(单条,多条,模糊)
def find_one(self, table, columns):
try:
sql = f"SELECT {', '.join(columns)} FROM {table}"
self.cursor.execute(sql)
return self.cursor.fetchone()
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
return None
def find_all(self, table, columns):
try:
sql = f"SELECT {', '.join(columns)} FROM {table}"
self.cursor.execute(sql)
return self.cursor.fetchall()
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
return None
def find_like(self, table, columns, conditions=None, params=None):
try:
sql = f"SELECT {', '.join(columns)} FROM {table}"
if conditions:
sql += f" WHERE {conditions}"
self.cursor.execute(sql, params or ())
return self.cursor.fetchall()
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
return None
# 2. 数据库数据的添加方法 -- 增(单条,多条)
def insert(self, table, data):
try:
columns = ', '.join(data.keys())
placeholders = ', '.join('?' for i in data)
sql = f"INSERT INTO {table} ({columns}) VALUES ({placeholders})"
self.cursor.execute(sql, tuple(data.values()))
self.conn.commit()
return self.cursor.lastrowid
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
self.conn.rollback()
return None
# 3. 数据库数据的修改方法 -- 改(根据条件)
def update(self, table, data, conditions=None, params=None):
try:
set_clause = ', '.join(f"{key} = ?" for key in data.keys())
sql = f"UPDATE {table} SET {set_clause}"
if conditions:
sql += f" WHERE {conditions}"
self.cursor.execute(sql, tuple(data.values()) + (params or ()))
self.conn.commit()
return self.cursor.rowcount
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
self.conn.rollback()
return None
# 4. 数据库数据的删除方法 -- 删(根据条件)
def delete(self, table, conditions=None, params=None):
try:
sql = f"DELETE FROM {table}"
if conditions:
sql += f" WHERE {conditions}"
self.cursor.execute(sql, params or ())
self.conn.commit()
return self.cursor.rowcount
except sqlite3.Error as e:
logging.error(f"报错的原因是: {e}")
self.conn.rollback()
return None
# 5. 关闭与数据库的连接 -- 关闭游标和连接
def close(self):
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
# 示例使用
obj = SQLiteManager('students.db')
obj.insert('students', {'name': '张三', 'age': 18, 'grade': 'A'})
obj.insert('students', {'name': 'anny', 'age': 28, 'grade': 'B'})
obj.delete("students", "id = ?", (2,))
# obj.update("students", {'name': '张三', 'age': 17, 'grade': 'A'}, "id = 1")
三、综合案例:基于 FastAPI 和 SQLAlchemy 实现CRUD接⼝
案例一:
需求:实现了⼀个基于FastAPI的省份信息管理API,包含创建、更新、删除和查询功能。使⽤SQLAlchemy操作SQLite数据库,通过Pydantic模型进⾏数据验证。每个API端点都包含数据库会话管理、参数校验和异常处理逻辑,确保数据操作的完整性和安全性。
python
# 导入FastAPI框架和HTTP异常处理模块
from fastapi import FastAPI, HTTPException
# 导入Pydantic的基模型,用于数据验证和序列化
from pydantic import BaseModel
# 导入SQLAlchemy的相关模块,用于数据库操作(create_engine:创建连接,Column:表示创建类属性,和数据库表字段形成映射,Integer, String:表示对应的类属性的数据类型)
from sqlalchemy import create_engine, Column, Integer, String #(创建数据库,创建表)
# sessionmaker:用于创建数据库会话工厂,方便管理数据库事务。
# declarative_base:用于生成ORM基类,定义数据库表结构的映射。
from sqlalchemy.orm import sessionmaker, declarative_base #(操作数据库,创建表)
# 映射的定义:映射在这里指的是将数据库中的表和数据转换为程序中的对象,方便操作。
# 初始化FastAPI应用
app = FastAPI()
# 数据库配置:
# 定义数据库的连接URL
DATABASE_URL = "sqlite:///./province.db"
# 创建数据库引擎,设置连接参数以允许在多线程环境中使用(创建数据库)
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) # check_same_thread=False,以允许跨线程操作数据库
# 创建一个会话类,用于生成数据库会话
# autocommit和autoflush设置为False,以确保事务的显式管理和数据的按需刷新
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) #bind=engine:操作的数据库目标
# 创建一个基类,用于后续定义数据库表模型
Base = declarative_base()
# 定义数据模型ORM类:(创建表的与句)
class Province(Base):
"""
省份模型类,继承自Base类。
这个类代表省份信息,映射到数据库中的"province"表。
"""
__tablename__ = "province" # 指定映射到的数据库表名
"""
省份ID字段,类型为整数,是表的主键,并且创建索引。
主键通常用于唯一标识表中的每一行记录。
"""
id = Column(Integer, primary_key=True, index=True)
"""
省份名称字段,类型为字符串,要求唯一,并且创建索引。
唯一约束确保省份名称不重复,索引提高查询效率。
"""
name = Column(String, unique=True)
"""
省份代码字段,类型为字符串。
用于存储省份的编码,便于数据的管理和查询。
"""
code = Column(String)
# 创建数据库表:据定义的数据库模型创建所有对应的数据库表(在这里指的是:Province)
Base.metadata.create_all(bind=engine) #bind=engine:相当于给了我一个创建表的路径(在哪一个数据库创键表)
# Pydantic模型用于请求和响应数据验证
class ProvinceCreate(BaseModel):
name: str
code: str
class ProvinceUpdate(BaseModel):
name: str
code: str
class ProvinceResponse(BaseModel):
id: int
name: str
code: str
# API接口实现
@app.post("/provinces/", response_model=ProvinceResponse) #response_model:功能是接收客户端发送的POST请求,并按照ProvinceResponse模型格式返回数据
def create_province(province: ProvinceCreate):
"""
增:创建新省份记录。
参数:
- province: ProvinceCreate 类型,包含要创建省份的信息。
返回:
- 创建的省份记录。
Raises:
- HTTPException: 如果省份已存在,抛出400错误。
"""
# 创建数据库会话
db = SessionLocal()
try:
# 检查省份是否已存在
# db.query(Province).filter(Province.name == province.name).first()
# 类似于: SELECT * FROM province WHERE name = '长沙'
existing_province = db.query(Province).filter(Province.name == province.name).first()
if existing_province:
# 如果省份已存在,抛出异常
raise HTTPException(status_code=400, detail="省份已存在")
# 插入新省份
db_province = Province(name=province.name, code=province.code)
db.add(db_province)
db.commit()
# 刷新实例,更新ID等信息
db.refresh(db_province)
return db_province
finally:
# 关闭数据库会话
db.close()
# 200:请求成功的状态码
# 404:页面找不到
# 422:请求体中数据有问题(格式不正确,名字匹配不对)
# 405:请求的方式不匹配(如路径是get形式,但是函数上面写的是其他的请求类型)
# 500:后台服务器程序出错d
@app.put("/provinces/{name}", response_model=ProvinceResponse)
def update_province(name: str, province_update: ProvinceUpdate):
"""
改:更新指定省份的信息。
参数:
- name: 省份名称,用于标识要更新的省份。
- province_update: 包含要更新的省份信息的对象。
返回:
- 更新后的省份信息对象。
"""
# 创建数据库会话
db = SessionLocal()
try:
# 查找省份
db_province = db.query(Province).filter(Province.name == name).first()
if not db_province:
# 如果省份不存在,抛出异常
raise HTTPException(status_code=404, detail="省份未找到")
# 更新省份信息
db_province.name = province_update.name
db_province.code = province_update.code
# 提交事务以保存更改
db.commit()
# 刷新实例以获取更新后的状态
db.refresh(db_province)
return db_province
finally:
# 关闭数据库会话
db.close()
@app.delete("/provinces/{name}", response_model=dict)
def delete_province(name: str):
"""
删除指定省份。
此函数通过接收省份名称作为参数,尝试从数据库中删除该省份记录。
如果省份不存在,则抛出404错误。此函数旨在为用户提供一个删除不需要的省份信息的方法。
参数:
- name (str): 要删除的省份名称。
返回:
- dict: 包含删除操作结果信息的字典。
"""
# 创建数据库会话
db = SessionLocal()
try:
# 查找省份
db_province = db.query(Province).filter(Province.name == name).first()
# 如果省份不存在,抛出404异常
if not db_province:
raise HTTPException(status_code=404, detail="省份未找到")
# 删除省份
db.delete(db_province)
# 提交数据库更改
db.commit()
# 返回删除成功的消息
return {"message": f"省份 {name} 已删除"}
finally:
# 关闭数据库会话
db.close()
@app.get("/provinces/", response_model=list[ProvinceResponse])
def get_all_provinces():
"""
查询所有省份信息。
该函数通过数据库查询,获取所有省份的信息,并返回一个省份信息列表。
使用了FastAPI的路径操作装饰器,指定HTTP GET方法和响应模型。
"""
# 创建数据库会话
db = SessionLocal()
try:
# 查询数据库中所有省份信息
provinces = db.query(Province).all()
return provinces
finally:
# 关闭数据库会话
db.close()
@app.get("/provinces/{name}", response_model=ProvinceResponse)
def get_province_by_name(name: str):
"""
根据名称查询省份信息。
参数:
name (str): 省份的名称。
返回:
ProvinceResponse: 省份信息响应对象。
异常:
HTTPException: 如果省份未找到,则抛出404错误。
"""
# 创建数据库会话
db = SessionLocal()
try:
# 查询省份信息
province = db.query(Province).filter(Province.name == name).first()
# 如果省份不存在,抛出异常
if not province:
raise HTTPException(status_code=404, detail="省份未找到")
# 返回省份信息
return province
finally:
# 关闭数据库会话
db.close()
if __name__ == "__main__":
import uvicorn
uvicorn.run("318SQLite.fastAPI_ORM:app", host="127.0.0.1", port=8000)
案例二:
需求:实现了⼀个基于FastAPI的省份信息管理API,包含用户注册、登录、更新、删除和查询功能。使⽤SQLAlchemy操作SQLite数据库,通过Pydantic模型进⾏数据验证。每个API端点都包含数据库会话管理、参数校验和异常处理逻辑,确保数据操作的完整性和安全性。
python
from fastapi import FastAPI,HTTPException
from pydantic import BaseModel
from requests import Session
from sqlalchemy import create_engine,Column,String,Integer
from sqlalchemy.orm import sessionmaker,declarative_base
app = FastAPI()
data_base = 'sqlite:///denglu.db'
engine = create_engine(data_base,connect_args={"check_same_thread":False})
SessionLocal = sessionmaker(autocommit=False,autoflush=False,bind=engine)
Base = declarative_base()
class User(Base):
__tablename__ = "MyLogin"
id = Column(Integer,primary_key=True,index=True)
username = Column(String,unique=True,index=True)
password = Column(String)
Base.metadata.create_all(bind=engine)
class UserCreate(BaseModel):
username: str
password: str
class UserUpdate(BaseModel):
username: str
password: str
class UserResponse(BaseModel):
id: int
username: str
password:str
# 注册:
@app.post("/register",response_model=UserResponse)
def create_user(user: UserCreate):
db = SessionLocal()
try:
existing_user = db.query(User).filter(User.username == user.username).first()
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
new_user = User(
username=user.username,
password=user.password
)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
finally:
db.close()
# 登录:
@app.post("/login",response_model=dict)
def login(form_data: UserCreate):
db = SessionLocal()
try:
existing_user = db.query(User).filter(User.username == form_data.username).first()
if not existing_user:
raise HTTPException(status_code=400, detail="用户名不存在,请重试!!!")
if existing_user.password != form_data.password:
raise HTTPException(status_code=400, detail="密码错误,请重试!!!")
return {"message":"登录成功"}
finally:
db.close()
# 更新:
@app.put("/update/{username}",response_model=UserResponse)
def update_user(username: str, user_update: UserUpdate):
db = SessionLocal()
try:
existing_user = db.query(User).filter(User.username == username).first()
if not existing_user:
raise HTTPException(status_code=400, detail="用户名不存在,请重试!!!")
existing_user.password = user_update.password
existing_user.username = user_update.username
db.commit()
db.commit()
db.refresh(existing_user)
return existing_user
finally:
db.close()
# 删除用户:
@app.delete("/delete/{username}",response_model=dict)
def delete_user(username: str):
db = SessionLocal()
try:
existing_user = db.query(User).filter(User.username == username).first()
if not existing_user:
raise HTTPException(status_code=400, detail="用户名不存在,请重试!!!")
db.delete(existing_user)
db.commit()
return {"message": f"用户 {username} 删除成功"}
finally:
db.close()
# 按名字查询:
@app.get("/get/{username}",response_model=UserResponse)
def get_user_name(username: str):
db = SessionLocal()
try:
existing_user = db.query(User).filter(
User.username == username
).first()
if not existing_user:
raise HTTPException(status_code=400, detail="用户名不存在,请重试!!!")
return existing_user
finally:
db.close()
# 查询所有用户:
@app.get("/get_all",response_model=list[UserResponse])
def get_all_users():
db = SessionLocal()
try:
users = db.query(User).all()
return users
finally:
db.close()
if __name__ == "__main__":
import uvicorn
uvicorn.run("318SQLite.my_longin:app", host='127.0.0.1', port=8000)
案例三:
需求:基于案例二的基础上,实现了⼀个基于FastAPI的省份信息管理API,包含用户注册、登录、更新、删除和查询功能。使⽤SQLAlchemy操作SQLite数据库,通过Pydantic模型进⾏数据验证。每个API端点都包含数据库会话管理、参数校验和异常处理逻辑,确保数据操作的完整性和安全性。
添加以下两个功能:1、登录时做token的校验,注册时做用户名重复校验;2、注册密码加密,登录时做密码解密校验。
python
# 导入FastAPI相关模块
from fastapi import FastAPI, HTTPException, Depends, status
# 导入Pydantic模型基类和配置,用于数据验证
from pydantic import BaseModel, ConfigDict
# 导入SQLAlchemy组件用于数据库操作
from sqlalchemy import create_engine, Column, Integer, String
# 导入会话管理器和声明式基类
from sqlalchemy.orm import sessionmaker, declarative_base
# 密码加密库
from passlib.context import CryptContext
# JWT操作库
from jose import JWTError, jwt
# 日期时间处理
from datetime import datetime, timedelta
# 可选类型注解
from typing import Optional
# OAuth2密码流安全相关
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
# 配置OAuth2密码流,指定获取token的URL端点
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 密码加密上下文配置,使用bcrypt算法
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# JWT配置(注意:ALGORITHM应为标准算法如HS256,此处存在错误)
SECRET_KEY = "ninglegedongdong" # 应保管好密钥,生产环境应使用安全随机字符串
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # 令牌有效期30分钟
# 创建FastAPI应用实例
app = FastAPI()
# 配置SQLite数据库连接字符串(当前目录生成Login.db文件)
my_database = 'sqlite:///Login.db'
# 创建数据库引擎,关闭线程检查以支持多线程
engine = create_engine(my_database,connect_args={"check_same_thread": False})
# 创建数据库会话工厂类(尚未实例化会话)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
# 声明式基类用于定义数据模型
Base = declarative_base()
# 定义用户数据模型
class User(Base):
__tablename__ = 'login' # 数据库表名
# 字段定义
id = Column(Integer, primary_key=True, index=True) # 主键ID
username = Column(String, unique=True, index=True) # 唯一用户名
hashed_password = Column(String) # 加密后的密码
# 在数据库中创建所有定义的表(如果不存在)
Base.metadata.create_all(bind=engine)
# --- Pydantic模型定义区 ---
class UserCreate(BaseModel):
"""用户创建请求模型"""
username: str
password: str
class UserResponse(BaseModel):
"""用户响应模型"""
id: int
username: str
# 允许从ORM对象转换(替代旧的Config类)
model_config = ConfigDict(from_attributes=True)
class UserUpdate(BaseModel):
"""用户更新模型"""
username: str
password: str
class Token(BaseModel):
"""令牌响应模型"""
access_token: str # JWT访问令牌
token_type: str # 令牌类型(通常为bearer)
class TokenData(BaseModel):
"""令牌数据模型"""
username: Optional[str] = None # 从令牌中解析的用户名
# --- 工具函数区 ---
def get_password_hash(password: str):
"""生成密码哈希值"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str):
"""验证密码与哈希是否匹配"""
return pwd_context.verify(plain_password, hashed_password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""生成JWT访问令牌"""
to_encode = data.copy() # 防止修改原始数据
# 计算过期时间
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
# 编码生成JWT
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme)):
"""获取当前用户的依赖项"""
# 定义认证异常
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="身份凭证验证失败",
headers={"WWW-Authenticate": "Bearer"},
)
try:
# 解码JWT令牌
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# 获取sub字段(通常存储用户名)
username: str = payload.get("sub")
if not username:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
# 数据库查询用户
db = SessionLocal()
try:
user = db.query(User).filter(
User.username == token_data.username
).first()
if user is None:
raise credentials_exception
return user
finally:
db.close() # 确保关闭会话
# --- 路由处理区 ---
# 注册:
@app.post('/register', response_model=UserResponse)
def create_user(user: UserCreate):
"""用户注册端点"""
db = SessionLocal()
try:
# 检查用户名是否存在
existing_user = db.query(User).filter(
User.username == user.username
).first()
if existing_user:
raise HTTPException(status_code=400, detail="用户名已存在")
# 创建新用户
hashed_password = get_password_hash(user.password)
new_user = User(
username=user.username,
hashed_password=hashed_password
)
db.add(new_user)
db.commit() # 提交事务
db.refresh(new_user) # 刷新获取数据库生成的值(如ID)
return new_user
finally:
db.close() # 确保关闭会话
# 更新:
@app.put('/login/{username}', response_model=UserResponse)
def update_user(
username: str,
user_update: UserUpdate,
current_user: User = Depends(get_current_user) # 依赖当前用户认证
):
"""更新用户信息"""
db = SessionLocal()
try:
# 查询目标用户
existing_user = db.query(User).filter(
User.username == username
).first()
if not existing_user:
raise HTTPException(status_code=404, detail="用户不存在")
# 验证输入数据(此处检查可能多余,因为Pydantic已做基础验证)
if not user_update.username or not user_update.password:
raise HTTPException(status_code=400, detail="无效的更新数据")
# 更新字段
existing_user.username = user_update.username
existing_user.hashed_password = get_password_hash(user_update.password)
db.commit()
db.refresh(existing_user)
return existing_user
except Exception as e:
db.rollback() # 回滚事务
raise HTTPException(status_code=500, detail=str(e))
finally:
db.close()
# 登录;
@app.post("/token", response_model=Token)
async def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends() # 不懂
):
"""登录获取访问令牌"""
db = SessionLocal()
try:
# 查询用户
user = db.query(User).filter(
User.username == form_data.username
).first()
# 验证用户是否存在及密码正确性
if not user or not verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
# 生成令牌
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
finally:
db.close()
# 删除:
@app.delete('/login/{username}', response_model=dict)
def delete_user(
username: str,
current_user: User = Depends(get_current_user) # 需要认证
):
"""删除用户"""
db = SessionLocal()
try:
existing_user = db.query(User).filter(
User.username == username
).first()
if not existing_user:
raise HTTPException(status_code=404, detail="用户不存在")
db.delete(existing_user)
db.commit()
return {'message': f'用户 {username} 删除成功'}
finally:
db.close()
# 按名字查询
@app.get('/login/{username}', response_model=UserResponse)
def get_user_name(
username: str,
current_user: User = Depends(get_current_user) # 需要认证
):
"""获取指定用户信息"""
db = SessionLocal()
try:
user = db.query(User).filter(
User.username == username
).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
finally:
db.close()
# 查询所有用户:
@app.get('/login', response_model=list[UserResponse])
def get_all_users(
current_user: User = Depends(get_current_user) # 需要认证
):
"""获取所有用户信息"""
db = SessionLocal()
try:
users = db.query(User).all()
return users
finally:
db.close()
# 启动服务(开发模式):
if __name__ == '__main__':
import uvicorn
uvicorn.run("318SQLite.Login:app", host='127.0.0.1', port=8000)
四、总结
本项目不仅适用于初学者快速上手FastAPI与SQLite,也为中高级开发者提供了完整的CRUD实现方案。通过本项目,您可以深入理解ORM、依赖注入、数据验证等核心概念,为后续开发更复杂的Web应用奠定坚实基础。
行动起来:将代码部署到您的开发环境中,尝试添加新功能(如文件上传、缓存机制),并将其上传到GitHub作为技术作品。在面试中,结合本项目的设计与实现思路,展现您的工程化思维与技术实力!
希望这篇指南能成为您API开发路上的得力助手!如有疑问,欢迎在评论区交流。