(进阶篇)Python web框架FastAPI——一个比Flask和Tornada更高性能的API 框架

简介:

前言

上一篇已经初步了解了 FastAPI 的基本使用,但是如果想要真正把 FastAPI 部署上线到服务器,那么你需要了解更多,学习更多。所以本篇内容将注重于 FastAPI 的项目生产环境,诸如 数据库,路由蓝图,数据验证等问题在 FastAPI 中的具体操作和一些自己碰到的坑,分享给正在进攻 FastAPI 的各位小伙伴。

蓝图

事实上,FastAPI 并没有关于蓝图 (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")

可以看到在 home 目录引入了 user.py 和 index.py 文件,注意必须要在文件中初始化一个 APIRouter() 类对象 (当然如果需要,可以选择继承),prefix 指明子路由的路径,更多的参数使用请参考官方文档。

user.py

from starlette.templating import Jinja2Templates
from 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 添加蓝图要轻松许多。

同时支持多种请求方式

在上面的 login 例子可以发现,我在上下文 request 中通过判断路由的请求方式来进行响应的逻辑处理,比如如果不是 Post请求 就把它重定向到 login 页面等等。那么就需要同时支持多种请求方式了,巧合的是,我在 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)

其中,methods 是非常熟悉的字眼,写入你想要的 http请求方式,path 指访问时的路径,endpoint 就是后端方法了。
这样就解决了同时存在于多个 http请求方式 的问题啦,编码也更为直观简洁。

数据库

在 FastAPI 中,我们一如既往的使用了 SQLAlchemy
初始化数据库文件:

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

创建数据库连接URI

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://root:123456@127.0.0.1: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})
这里是一些示例的 crud,真正部署的时候可不能这么鲁莽哇,错误的捕捉,数据库的回滚,语句必须严谨。

数据验证

在路由方法中,有个叫 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 模型。

场景的话,可以想象用户登陆时需要传入用户名和密码,用户登陆成功之后在首页上展示用户名的邮件,不展示密码。嗯,这样就合理了。

所以在数据库操作的时候,可以自己定义传入和返回的模型字段来做有效的限制,你只需要继承 pydantic 中的 BaseModel 基类即可,看起来是那么的简单合理。

异常处理

在各种 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
相关文章
|
7月前
|
开发框架 前端开发 Go
【GoGin】(0)基于Go的WEB开发框架,GO Gin是什么?怎么启动?本文给你答案
Gin:Go语言编写的Web框架,以更好的性能实现类似Martini框架的APInet/http、Beego:开源的高性能Go语言Web框架、Iris:最快的Go语言Web框架,完备的MVC支持。
605 1
|
安全 前端开发 数据库
Python 语言结合 Flask 框架来实现一个基础的代购商品管理、用户下单等功能的简易系统
这是一个使用 Python 和 Flask 框架实现的简易代购系统示例,涵盖商品管理、用户注册登录、订单创建及查看等功能。通过 SQLAlchemy 进行数据库操作,支持添加商品、展示详情、库存管理等。用户可注册登录并下单,系统会检查库存并记录订单。此代码仅为参考,实际应用需进一步完善,如增强安全性、集成支付接口、优化界面等。
|
11月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:路由、中间件、参数校验
Gin框架以其极简风格、强大路由管理、灵活中间件机制及参数绑定校验系统著称。本文详解其核心功能:1) 路由管理,支持分组与路径参数;2) 中间件机制,实现全局与局部控制;3) 参数绑定,涵盖多种来源;4) 结构体绑定与字段校验,确保数据合法性;5) 自定义校验器扩展功能;6) 统一错误处理提升用户体验。Gin以清晰模块化、流程可控及自动化校验等优势,成为开发者的优选工具。
|
11月前
|
开发框架 安全 前端开发
Go Web开发框架实践:模板渲染与静态资源服务
Gin 是一个功能强大的 Go Web 框架,不仅适用于构建 API 服务,还支持 HTML 模板渲染和静态资源托管。它可以帮助开发者快速搭建中小型网站,并提供灵活的模板语法、自定义函数、静态文件映射等功能,同时兼容 Go 的 html/template 引擎,具备高效且安全的页面渲染能力。
|
11月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:使用 Gin 快速构建 Web 服务
Gin 是一个高效、轻量级的 Go 语言 Web 框架,支持中间件机制,非常适合开发 RESTful API。本文从安装到进阶技巧全面解析 Gin 的使用:快速入门示例(Hello Gin)、定义 RESTful 用户服务(增删改查接口实现),以及推荐实践如参数校验、中间件和路由分组等。通过对比标准库 `net/http`,Gin 提供更简洁灵活的开发体验。此外,还推荐了 GORM、Viper、Zap 等配合使用的工具库,助力高效开发。
|
人工智能 自然语言处理 JavaScript
测试工程师要失业?Magnitude:开源AI Agent驱动的端到端测试框架,让Web测试更智能,自动完善测试用例!
Magnitude是一个基于视觉AI代理的开源端到端测试框架,通过自然语言构建测试用例,结合推理代理和视觉代理实现智能化的Web应用测试,支持本地运行和CI/CD集成。
2147 15
测试工程师要失业?Magnitude:开源AI Agent驱动的端到端测试框架,让Web测试更智能,自动完善测试用例!
|
人工智能 搜索推荐 IDE
突破网页数据集获取难题:Web Unlocker API 助力 AI 训练与微调数据集全方位解决方案
本文介绍了Web Unlocker API、Web-Scraper和SERP API三大工具,助力解决AI训练与微调数据集获取难题。Web Unlocker API通过智能代理和CAPTCHA绕过技术,高效解锁高防护网站数据;Web-Scraper支持动态内容加载,精准抓取复杂网页信息;SERP API专注搜索引擎结果页数据抓取,适用于SEO分析与市场研究。这些工具大幅降低数据获取成本,提供合规保障,特别适合中小企业使用。粉丝专属体验入口提供2刀额度,助您轻松上手!
793 2
|
人工智能 运维 安全
网络安全公司推荐:F5荣膺IDC全球Web应用与API防护领导者
网络安全公司推荐:F5荣膺IDC全球Web应用与API防护领导者
394 4
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
机器学习/深度学习 开发框架 API
Python 高级编程与实战:深入理解 Web 开发与 API 设计
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧以及数据科学和机器学习。本文将深入探讨 Python 在 Web 开发和 API 设计中的应用,并通过实战项目帮助你掌握这些技术。

热门文章

最新文章

推荐镜像

更多