(高度)Python WebフレームワークFastAPI-FlaskやTornadaよりも高性能なAPIフレームワーク

序文

 

前回の記事では、FastAPIの基本的な使用法を予備的に理解していましたが、FastAPIを実際にサーバーに展開したい場合は、さらに詳しく学ぶ必要があります。したがって、この記事では、データベースでのFastAPIの特定の操作、ルーティングブループリント、データ検証、自分が遭遇したピットなど、FastAPIプロジェクトの本番環境に焦点を当て、FastAPIを攻撃しているすべての人と共有します。

 

 

ブループリント

 

実際、FastAPIにはBlueprintの定義がありません。Blueprintとも呼ばれるFastAPIにルートを追加するには、Include_routeメソッドを使用します。

import time
from typing import List
from starlette.templating import Jinja2Templates
from fastapi import Depends, FastAPI, HTTPException
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates
from app import models
from app.database.database import SessionLocal, engine
from app.home import user,index


app = FastAPI()

app.mount("/static", StaticFiles(directory="app/static"), name="static") # 挂载静态文件,指定目录
templates = Jinja2Templates(directory="templates") # 模板目录

app.include_router(index.userRouter)
app.include_router(user.userRouter,prefix="/user")

 

user.pyファイルとindex.pyファイルがホームディレクトリに導入されていることがわかります。APIRouter()クラスオブジェクトはファイル内で初期化する必要があります(もちろん、必要に応じて継承することもできます)。プレフィックスはサブルーチンのパスを示します。パラメータの使用については、公式ドキュメントを参照してください。

# user.pyfrom starlette.templating import Jinja2Templatesfrom app import schemas, models
from app.database.database import get_db
from app.home import crud
from fastapi import Depends, HTTPException, Form
from sqlalchemy.orm import Session
from app.models import User
from sqlalchemy.orm import Session
from fastapi import APIRouter, HTTPException,Request
from fastapi.responses import RedirectResponse

userRouter = APIRouter()
templates = Jinja2Templates(directory="app/templates") # 模板目录

@userRouter.post("/login/", response_model=schemas.UserOut)
async def login(*,request: Request,db: Session = Depends(get_db), username: str = Form(None), password: str = Form(None),):
    if request.method == "POST":
        db_user = db.query(models.User).filter(User.username == username).first()
        if not db_user:
            raise HTTPException(status_code=400, detail="用户不存在")
        print("验证通过 !!!")
        return RedirectResponse('/index')

    return templates.TemplateResponse("user/login.html", {"request": request})

 

Flaskがブループリントを追加するよりもはるかに簡単に見えます。

 

同時に複数のリクエストメソッドをサポート

 

上記のログインの例では、コンテキストリクエストで、ルートリクエストメソッドを判断することで応答の論理処理を実行していることがわかります。たとえば、Postリクエストでない場合は、ログインページにリダイレクトします。次に、同時に複数のリクエストメソッドをサポートする必要があります。偶然にも、FastAPIのドキュメントに対応する手順が見つかりません。最初はしばらく混乱していました。したがって、ソースコードしか実行できません。

 

APIRouterクラスが配置されているファイルに直接移動して、新世界を発見します。

 

 

APIRouterの下にadd_api_routeというメソッドがあり、リストとして渡されるhttpメソッドをサポートしているため、次のように置き換えます。

async def login(*,request: Request,db: Session = Depends(get_db), username: str = Form(None), password: str = Form(None),):
    if request.method == "POST":
        db_user = db.query(models.User).filter(User.username == username).first()
        if not db_user:
            raise HTTPException(status_code=400, detail="用户不存在")
        print("验证通过 !!!")
        return RedirectResponse('/index')

    return templates.TemplateResponse("user/login.html", {"request": request})


async def userList(*,request: Request,db: Session = Depends(get_db)):
    userList = db.query(models.User).all()
    return templates.TemplateResponse("user/user-index.html", {"request": request,'userList':userList})

userRouter.add_api_route(methods=['GET','POST'],path="/login",endpoint=login)
userRouter.add_api_route(methods=['GET','POST'],path="/list",endpoint=userList)

 

その中でも、メソッドは非常に馴染みのある言葉であり、必要なHTTPリクエストメソッドを記述し、パスはアクセス時のパスを指し、エンドポイントはバックエンドメソッドです。

 

これにより、複数のHTTPリクエストメソッドの問題が同時に解決され、コーディングがより直感的で簡潔になります。

 

データベース

 

FastAPIでは、いつものようにSQLAlchemyを使用しました

 

データベースファイルを初期化します。

from sqlalchemy import create_enginefrom sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# 创建数据库连接URI
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:[email protected]:3306/blog"

# 初始化
engine = create_engine(
    SQLALCHEMY_DATABASE_URL
)

# 创建DBSession类型
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# 创建基类 用于继承  也可以放到初始化文件中
Base = declarative_base()

# 获取数据库会话,用于数据库的各种操作
def get_db():
    db = SessionLocal()

 

データベースモデルファイル:

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, DateTime, Text
from sqlalchemy.orm import relationship
from datetime import datetime
from flask_login import  UserMixin
import uuid
from app.database.database import Base


class User(UserMixin,Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    email = Column(String(64),)
    username = Column(String(64), )
    role = Column(String(64), )
    password_hash = Column(String(128))
    head_img = Column(String(128), )
    create_time  = Column(DateTime,default=datetime.now)

    def __repr__(self):
        return '<User %r>' % self.username

# 文章表
class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True)
    title=Column(String(32))
    author =Column(String(32))
    img_url = Column(Text,nullable=False)
    content=Column(Text,nullable=False)
    tag=Column(String(64),nullable=True)
    uuid = Column(Text,default=uuid.uuid4())
    desc = Column(String(100), nullable=False)
    create_time = Column(DateTime,default=datetime.now)
    articleDetail = relationship('Article_Detail', backref='article')

    def __repr__(self):
        return '<Article %r>' % self.title

 

増やす

async def articleDetailAdd(*,request: Request,db: Session = Depends(get_db),d_content:str,uid:int):
    if request.method == "POST":
        addArticleDetail= Article_Detail(d_content=d_content,uid=uid)
        db.add(addArticleDetail)
        db.commit()
        db.refresh(addArticleDetail)
        print("添加成功 !!!")
        return "添加成功"
    return "缺少参数"

 

削除する

async def articleDetailDel(*,request: Request,db: Session = Depends(get_db),aid:int):
    if request.method == "POST":
        db.query(Article_Detail).filter(Article_Detail.id == aid).delete()
        db.commit()
        print("删除成功 !!!")
        return "删除成功"
    return "缺少参数"

 

変化

async def articleDetailUpdate(*,request: Request,db: Session = Depends(get_db),aid:int,d_content:str):
    if request.method == "POST":
        articleInfo= db.query(Article_Detail).filter(Article_Detail.id == aid).first()
        print(articleInfo)
        if not articleInfo:
            raise HTTPException(status_code=400, detail="no no no !!")

        articleInfo.d_content = d_content
        db.commit()
        print("提交成功 !!!")
        return "更新成功"
    return "缺少参数"

 

チェックする

async def articleDetailIndex(*,request: Request,db: Session = Depends(get_db),):
    articleDetailList = db.query(models.Article_Detail).all()
    return templates.TemplateResponse("articleDetail/articleDetail-index.html", {"request": request,"articleDetailList":articleDetailList})

 

クラッドの例をいくつか示します。実際にデプロイした場合、それほど無謀なことはできません。間違ったキャプチャ、データベースのロールバック、ステートメントは厳密でなければなりません。

 

データの検証

 

ルーティングメソッドには、response_modelというパラメーターがあり、ルーティングメソッドの戻りフィールドを制限するために使用されます。

 

公式文書の例:

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
    username: str
    password: str
    email: EmailStr
    full_name: str = None


class UserOut(BaseModel):
    username: str
    email: EmailStr
    full_name: str = None


@app.post("/user/", response_model=UserOut)
async def create_user(*, user: UserIn):
    return user

 

これは、UserInがリクエストボディパラメータとして渡され、戻るときにUserOutモデルに戻る必要があることを意味します。

シナリオでは、ユーザーがログイン時にユーザー名とパスワードを渡す必要があると想像できます。ユーザーが正常にログインすると、ホームページにユーザー名の電子メールが表示され、パスワードは表示されません。まあ、これは理にかなっています。

 

そのため、データベース操作中に、効果的に制限するために受信モデルフィールドと返されたモデルフィールドを定義できます。

 

例外処理

 

さまざまなhttpリソースが存在しないか、例外にアクセスする場合は、httpステータスコードと例外の説明が必要です。たとえば、404 Not Foundエラー、Postリクエスト422、サーバー500エラーなど、プログラムで例外を合理的に発生させる方法非常に重要になります。

 

FastAPIで例外処理を使用する方法を見る

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}


@app.get("/items/{item_id}")
async def read_item(item_id: str):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": items[item_id]}

 

HTTPExceptionを使用して、ステータスコードと詳細な手順を渡し、論理エラーが発生したときに例外をスローします。

 

HTTPExceptionを書き換えます

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse


class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name


app = FastAPI()


@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
    return JSONResponse(
        status_code=418,
        content={"message": f"我家热得快炸了..."},
    )


@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
    if name == "yolo":
        raise UnicornException(name=name)
    return {"unicorn_name": name}

 

UnicornExceptionは、Pythonに付属のExceptionクラスを継承しています。サーバー側エラーが発生すると、418エラーがスローされ、エラーの説明が添付されます。

 

独自の例外処理コードをカスタマイズする

from fastapi import FastAPI, HTTPException
from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()


@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
    return PlainTextResponse(str(exc.detail), status_code=exc.status_code)


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return PlainTextResponse(str(exc), status_code=400)


@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id == 3:
        raise HTTPException(status_code=418, detail="开空调啊")
    return {"item_id": item_id}

 

例外処理メカニズムを適切に使用すると、プロジェクトコードがより堅牢になり、クライアントがより使いやすく、保守が容易になります。

 

他に何かありますか?

 

広大なFastAPIドキュメントで、使いやすく実用的で使いやすい関数を見つけて共有し、実際の本番環境に投資してみました。このプロセスで、多くのことを学び、より良い経験をしましたサービスのパフォーマンス。

FastAPIの公式ドキュメントは非常に大きく、FastAPIの安全な暗号化、ミドルウェアの使用、アプリケーションのデプロイなど、普及していない多くの場所があります。ハ、日本に来てね!

 

FastAPIについてさらに学ぶ必要がある場合は、詳細を読むためにすべてを読むことができます。

参照ドキュメント:https://fastapi.tiangolo.com/tutorial

おすすめ

転載: www.cnblogs.com/dcpeng/p/12716560.html