随着python的发展,也出现了越来越多的框架,如Django,Falsk,Bottle框架等,FastApi就是其中之一,同时,也能够进行异步操作,async/await,这里借用官网的一句话FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.8+ 并基于标准的 Python 类型提示。具体的特性也在官网上有所讲述,首先使用的话需要有python环境,可以去官网上进行安装,python环境搭建还是较为简单的,直接去python官网进行安装就可以了,安装好后,同时也需要python的编辑器,这里我使用的是PyCharm,可以自行去官网下载,在搭建好使用环境后,同时也不能忘记去配置环境变量,这里具体的配置方式可以自行搜索
这里直接使用
pip install fastapi
来安装fastapi,以及服务器:
arduino
pip install "uvicorn[standard]"
这里我们可以使用PyCharm自带的工程创建来创建一个fastapi的工程,同时可以去选择使用虚拟环境去创建,可以避免无法管理相关的python包
然后通过
css
uvicorn main:app --reload
来启动我们的服务器
如果是用过PyCharm创建的话,我们的根路径下的入口文件就会有:
python
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
return {"item_id": item_id, "q": q}
如果没有的话,大家可以自行创建一下,并且fastapi有继承自swagger文档,可以通过访问 http://127.0.0.1:8000/docs来自行查看api文档,同时也可以自行设置相关的配置,下一张再来详细的讲一下,数据库的配置以及如何通过orm模型操作数据库
2.FastAPI如何链接数据库
这里使用的是mysql,基本的mysql环境搭建大家可以自行去网上搜索,这里不再说明,接下来直接上代码:
1、首先需要安装tortoise-orm,我们是通过orm模型去操作数据库,使用orm模型操作的好处是不需要使用sql命令,直接使用函数进行操作,能有效的防止sql注入式攻击,操作也会简洁一些,
pip install tortoise
2、接下来需要在入口文件进行导入和挂载:
ini
from tortoise.contrib.fastapi import register_tortoise
register_tortoise(
app,
db_url='mysql://用户名:密码@数据库ip:数据库端口/用户名',
modules={"models": ["models.user", "models.article"]},
generate_schemas=True,
add_exception_handlers=True,
)
这里的路径我给大家标注了一下,自己配置的时候要记得更换哦
剩下的配置分别是:
- generate_schemas:是否自动创建数据库和表结构,这里设置为 True,表示在启动应用时如果相关表不存在,则会自动创建。
- add_exception_handlers:是否添加 Tortoise ORM 的异常处理器
这里的app,是我们的fastapi主程序
3、接下来要说明一下modules了,这里的modules是我们存放模型的地方,是使用类来进行创建的表:
ini
from tortoise import Model, fields
class Article(Model):
userid = fields.IntField()
id = fields.IntField(pk=True)
title = fields.CharField(max_length=30)
content = fields.CharField(max_length=9999)
createTime = fields.DatetimeField(auto_now_add=True)
updateTime = fields.DatetimeField(auto_now=True)
这样我们在启动当前服务的时候,就会通过tortoise,来创建一张表了
然后我们就可以这么去操作:
less
@app.post("/announce", summary='发布文章')
async def article_announce(body: Announce):
await Article(userid=token, title=body.title, content=body.content).save()
return JSONResponse(status_code=status.HTTP_200_OK)
这就是最简单的往数据库中添加数据了,这里的Announce类呢是:
python
from pydantic import BaseModel, Field
class Announce(BaseModel):
title: str = Field(examples=["标题"])
content: str = Field(examples=["内容"])
这里我们需要从pydantic模型库中去导入我们需要继承的类,以及属性
3.FastAPI------token验证
其实fastapi自身对于token就有验证的,不过这里需要对用户输入的用户名和密码的参数限制为username,password,这对于我们来说有些限制了,比如我们在写微信小程序,或者APP登录时,我们一般不会去使用这种格式,并且在数据库中存入的参数形式也不一致,这样子对于我们来说就不是很有友好了,所以我们这里选择使用token,在使用的时候,我们需要先导入jwt,使用jwt来进行加密以及验证:
pip install jwt
然后需要使用加密字符,这里我没有使用他们的标准生成密钥方式,这里可以使用:
ini
import os
key = os.urandom(32)
这种方式来进行密钥的生成,规范的生成模式还是以官网的那种方式去生成
ini
def jwt_token(data: dict, expires_delta: timedelta):
jwt_dict = data.copy()
expired = datetime.utcnow() + expires_delta
jwt_dict.update({'exp': expired})
token = jwt.encode(jwt_dict, key, algorithm="HS256")
return token
这里我们先不说这个函数的,我们先来说
ini
jwt.encode(jwt_dict, key, algorithm="HS256")
# 这里的jwt_dict 是我们的加密数据以及过期时间等数据组成的字典,这里的key,是我们的加密密钥,然后
# algorithm是加密方式
python
datetime.utcnow() + expires_delta
# 这一步是格式化时间 在此之前,我们需要导入
from datetime import datetime, timedelta
然后我们再回到函数中,data为加密数据,是我们需要再登录时去处理的用户数据,然后expires_delta为过期时间,然后我们再回到我们的接口中去
ini
@api.post("/login", summary='用户登录')
async def user_login(body: Register):
res = await User.filter(userName=body.userName)
if len(res) == 0:
return JSONResponse(status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
content=APIResponse(405, None, "用户不存在").set_api_dict())
else:
md5password = password_md5(body.passWord)
password = await User.get(userName=body.userName)
if md5password == password.passWord:
token = jwt_token({"userID": password.id}, timedelta(days=3))
return JSONResponse(status_code=status.HTTP_200_OK, content=APIResponse(200, {"token": token}, "登录成功")
.set_api_dict(), headers={"Set-Cookie": "X-token=Bearer "+token})
else:
return JSONResponse(status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
content=APIResponse(405, None, "用户名或密码有误").set_api_dict())
这里的APIResponse是我自己定义的一个函数,用来处理返回数据的,大家这里可以自行忽略,同时我这里也使用了md5加密的一个形式,在前后端交互中,前端也可以处理加密,后端也可以,这个可以大家自行沟通,并且我这里也只是提供一个逻辑而已 ,后续我们再讲一下token的验证如何处理
4.FastAPI中的token验证
在fastapi中,我们可以通过依赖注入的方式,来对一个接口进行token的验证,来判断在数据库中是否有这个用户,我们需要先申明一个函数用来做token的验证操作:
python
import jwt
import os
from fastapi import Header
from typing import Optional
from .APIResponse import APIResponse
from fastapi.responses import JSONResponse
from fastapi import status
from jwt.exceptions import InvalidSignatureError, ExpiredSignatureError, DecodeError
key = os.urandom(32)
async def verify_jwt(token: Optional[str] = Header(None)):
if token is None:
return JSONResponse(status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
content=APIResponse(405, None, "用户未登录").set_api_dict())
else:
try:
res = jwt.decode(token, key, "HS256")
return res['userID']
except InvalidSignatureError as e:
# 无效签名验证
return JSONResponse(status_code=status.HTTP_403_FORBIDDEN,
content=APIResponse(403, None, "无效的用户签名").set_api_dict())
except ExpiredSignatureError as e:
# 签名过期验证
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED,
content=APIResponse(401, None, "用户签名已过期").set_api_dict())
except DecodeError as e:
return JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED,
content=APIResponse(401, None, "错误的签名").set_api_dict())
在这里我们将错误类型进行了导入,其实应该还有一部分其他类型的错误,我这里只是简单使用了一下,然后指定token类型为str,并且导入Header,将从这里获取我们的token,然后并对token解码进行验证,这样,就完成了我们的token验证函数
这里我也将我申明的APIResponse为返回给前端的数据:
python
import json
class APIResponse:
def __init__(self, code, data=None, message=None):
self.code = code
self.data = data
self.message = message
def set_api_dict(self):
api_dict = {
"code": self.code,
"data": self.data,
"message": self.message
}
return api_dict
然后在接口中使用:
python
from fastapi import status, Depends
from models.user import User
from fastapi.responses import JSONResponse
from utils.APIResponse import APIResponse
from utils.JWT import jwt_token, verify_jwt
@api.get("/logout", summary="用户注销")
async def user_logout(token: str = Depends(verify_jwt)):
if type(token) is int:
res = await User.get(id=token)
await res.delete()
return JSONResponse(status_code=status.HTTP_200_OK, content=APIResponse(200, None, "用户注销成功").set_api_dict())
else:
return token
在这里我们需要导入Depends,将我们的验证函数进行注入至接口,这样fastapi在前端进行请求的时候会自动运行这个函数verify_jwt,这样就是一个简单的token验证了。