fastAPI学习笔记(四)

中间件

创建中间件 使用装饰器 @app.middleware('http')
中间件接收两个参数

  • request:Request
  • call_next -> call_next接收request参数,此函数会传递request给相应的路径操作,然后返回response
    最后返回 response
@app.middleware('http')
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers['X-Process-Time'] = str(process_time)
    return response

添加现成中间件,可以使用 app.add_middleware(HTTPSRedirectMiddleware)

HTTPSRedirectMiddleware 是强制所有传入请求必须为https或wss 的中间件
CORSMiddleware 跨域资源共享配置的中间件

CORS(跨域资源共享)
from fastapi.middleware.cors import CORSMiddleware

origins = [
    "http://localhost.tiangolo.com",
    "https://localhost.tiangolo.com",
    "http://localhost",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_methods=['*'],
    allow_headers=['*'],
)

CORSMiddleware 默认参数是限制性的,因此需要显式的启用特定的来源,方法,或者标头
支持以下参数:

  • allow_origins 允许进行跨域请求的来源列表 ['*'] 允许任何来源
  • allow_origins_regex 正则匹配允许跨域请求的来源url
  • allow_methods 跨域请求允许的http方法列表 默认为['GET']
  • allow_headers 跨域请求允许的http标头列表 默认为[] ['*']允许所有标头
  • allow_credentials 跨域请求是否支持cookie 默认False
  • expose_handers 浏览器可以访问的任何响应头 默认为[]
  • max_age 设置浏览器缓存cors响应的最长时间(秒为单位) 默认600
SQL数据库
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

MYSQL_USER = ''
MYSQL_PASS = ''
MYSQL_HOST = ''
MYSQL_PORT = '3306'
MYSQL_DB = ''

SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASS}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}'
#SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"  # 配置数据库url
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(SQLALCHEMY_DATABASE_URI)  # 初始化数据库连接
#engine = create_engine(
#    SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}   # connect_args={"check_same_thread": False} 仅用于sqlite
#)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)  # 会话工厂

Base = declarative_base()  # 模型的基类

# 编写model models.py
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True, index=True)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)

    items = relationship("Item", back_populates="owner")

# 编写序列化器 schemas.py
class User(BaseModel):
    id: Optional[int] = None
    username: str
    email: str

    class Config:
    orm_mode = True  # 配置orm_mode除了可以让pydantic读取字典类型的数据,还支持读取属性数据,比如sqlalchemy模型的数据
                     # 这样pydantic数据模型就可以兼容sqlalchemy数据模型,可以直接在路径操作函数中直接返回sqlalchemy数据模型

# orm操作
add 添加对象到数据库会话
commit 提交对数据库的更改
refresh 刷新实例(以便包含数据库中的任何新数据,例如生成的id)
def create_user(db: Session, user: schemas.UserCreate):
    fake_hashed_password = user.password
    db_user = models.User(email=user.email hashed_password=fake_hashed_password)
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

# 获取数据库会话  使用完成自动关闭会话
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

create_engine 创建一个带连接池的引擎
创建一个session,连接池会分配一个connection.当session使用后需要显式的调用session.close(),commit()或者session.rollback()来把连接
归还到QueuePool连接池管理并复用连接
在使用create_engine创建引擎时,如果默认不指定连接池设置的话,一般情况下,SQLAlchemy 会使用一个 QueuePool 绑定在新创建的引擎上。并附上合适的连接池参数
create_engine()函数和连接池相关的参数有:

  • pool_recycle, 默认-1, 推荐设置7200,即如果connection空闲来7200秒,自动重新获取,防止connection被db server关闭
  • pool_size 连接数大小,默认为5,正式环境该数值太小,需根据实际情况调整
  • max_overflow 超出pool_size后可允许的最大连接数,默认10, 这 10 个连接在使用过后,不放在 pool 中,而是被真正关闭。
  • pool_timeout 获取连接的超时阈值,默认30秒
    不使用连接池: 添加参数 poolclass=NullPool 在create_engine中添加此参数

sessionmaker创建了一个工厂类,在创建这个工厂类时我们配置了参数绑定了引擎,将其赋值给SessionLocal,
每次实例化SessionLocal都会创建一个绑定了引擎的SessionLocal,这样这个SessionLocal在访问数据库时都会通过这个绑定好的引擎来获取连接资源

pydantic模型可以传入额外参数
Item(**item.dict(), owner_id=user_id)

创建数据库表

Base.metadata.create_all(bind=engine)

创建依赖 -> 一个能自动释放的session方法, sessionLocal在单个请求中使用,请求完成后被关闭

python3.6版本需要安装
pip install async-exit-stack async-generator

def get_db():
    db = sessionLocal()
    try:
        yield db
    finally:
        db.close()

# 使用它:
def read_items(db: Session = Depends(get_db)):
    ...

# 如果使用不了yield也装不了插件
可以使用中间件替代
@app.middleware('http')
async def db_session_middleware(request: Request, call_next):
    response = Response("Internal server error", status_code=500)
    try:
        request.state.db = SessionLocal()
        response = await call_next(request)
    finally:
        request.state.db.close()
    request response

def get_db(request: Request):
    return request.state.db
最好使用yield

db 参数实际上是SessionLocal类型,其实也是代理sqlalchemy的session, 声明成session让编译器能提供更好的支持 类型声明不影响实际对象

路由
APIRouter
from fastapi import APIRouter
router = APIRouter()

@router.get('item')
async def read_users():
    xxxxx

如果有相同的路径需要集合在一个文件夹 如: /items/, /items/{item_id}/, 这类的相同的/items/路径
可以使用
router = APIRouter(
    prefix='/items', # 统一的路径
    tags=['items']  # 标签 在文档中有作用
    dependencies=[Depends(get_token_header等等))]  # 注入一些无返回值的依赖
    response={404: {'description': 'not found'}},  # 特定的response返回
)
之后的路径操作:
@router.get('/')  # /items/
async def xxx():
    xxx

主文件的配置:
app.include_router(users.router)
app.include_router(
    admin.router,
    prefix="/admin",
    tags=["admin"],
    dependencies=[Depends(get_token_header)],
    responses={418: {"description": "I'm a teapot"}},
)
可以嵌套include_router
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容