# flask-api-demo
**Repository Path**: xjmroot/flask-api-demo
## Basic Information
- **Project Name**: flask-api-demo
- **Description**: 整合一个基于flask的api对接项目demo。
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2023-05-10
- **Last Updated**: 2023-05-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
##
flask-api-demo
一个基于flask架构的 `接口/数据对接` 小型项目开发结构模板。
封装了一些必要插件如`日志记录,定时任务,数据库操作,自定义错误,在线接口文档等`
### 项目结构介绍
├───app 程序主体
│ ├───module 项目代码位置
│ │ └───项目 *自定义的项目*
│ ├───plugin 通用功能插件
│ │ └───__init__.py 定义所有插件对外接口方法
| | └───com_method.py 一些可能用得上的通用方法记录在这
│ │ └───db.py 数据库操作封装
│ │ └───exception.py 自定义异常封装
│ │ └───falsk_docs.py 在线接口文档自定义封装
│ │ └───logger.py 日志封装
│ │ └───myrandom.py 随机数封装,包括汉字,英文大小写随机
│ │ └───mytime.py 时间格式转化封装
│ │ └───resp.py 通用接口返回封装
│ │ └───scheduler.py 定时任务封装
│ ├───base.py 副主体文件,全局的一些定义和配置
│ ├───config_apidoc.yml 在线文档的配置文件
│ ├───config.py 配置文件读取器
│ ├───config.yml 配置文件
│ ├───main.py 主体文件,为了精简,将部分主体代码转移到base.py
│ ├───nb_log_config.py 日志配置文件,nb_log包自带,不建议修改
├───.gitignore git 提交忽略文件
├───data.db 本地数据库文件
├───requirements.txt 项目运行环境依赖包
├───README.md 项目说明文件
### 运行
```cmd
python ./app/main.py
```
### 打包
```
pip install pyinstaller
# 注意flask_docs的路径,不同电脑会不同
# --add-data表示打包资源文件,静态文件需要额外打包
pyinstaller --add-data "C:\Python310\Lib\site-packages\flask_docs;flask_docs" --add-data "./app/view;view" -F ./app/main.py [-n name]
```
可以将程序打包成exe文件,注意,静态文件不会打包进去,需要手动将config.yml,config_apidoc.yml,data.db放到exe文件同目录进行运行
### 使用介绍
#### 新增对接一个项目
在module路径下,新建一个项目文件夹,里面可以任意实现。
推荐在自定义文件夹下面新建一个__init__.py,引入项目的运行主体方法。如果是接口,引入蓝图。
main.py里面import对应的项目,并调用方法主体/引用蓝图即可。
**例:**
```python
## file:module/demo/demo_handle.py
from flask import Blueprint
demo = Blueprint('demo',__name__,url_prefix='/demo')
# 接口示例
@demo.route('/test')
def test():
return 'success'
# 方法示例
def demo_main():
sync_data() # 省略实现,假设是实现一个数据同步
-----------------------------------------------------------------
## file:module/demo/__init__.py
from module.demo.demo_handle import demo_main,demo
demo_main # 方法主体
demo # 接口蓝图
-----------------------------------------------------------------
## file: main.py
## 修改如下:
# coding: utf-8
""" 运行文件,尽量简洁 """
from base import app, register_blueprints
from module.demo import demo_main,demo # 这里引用项目
@app.route('/')
def home():
return 'welcome to saftop api module home page'
if __name__ == '__main__':
demo_main() # 执行方法主体
app.config['BluePrints'] = [demo] # 所有蓝图放到这里面来,下面的方法会读取配置并注册蓝图
app.config['API_DOC_MEMBER'] = [demo] # 所有希望生成接口文档得蓝图放到这里来
register_blueprints()
app.run(host='0.0.0.0', port=5051)
```
### 插件介绍
所有插件对外开放的接口方法,定义到plugin.\_\_init__.py里面,引用时使用 from plugin imort xxx,不要直接引用文件
#### 数据库操作
`目前封装了sqlserver、mysql、sqlite以及redis`
使用了dbutils的线程池,默认5:min--10:max个数据库的短连接,使用次数为100。可以通过传参自定义。
线程池根据数据库连接生成,不同的数据库连接在第一次初始化时都会建立线程池,多次初始化同一个数据库连接不会影响
以sqlserver为例介绍功能
```python
"""
# 初始化连接对象
# 线程池变量以host+port+database+user+password+charset为key,数据库连接为value
# 同一个数据库连接可以随意实例化,不会影响连接和效率
"""
class MSsqlClient(host, port, database, user, password, charset='utf8',
mincached=5, maxcached=10, maxconnections=100, blocking=True, maxusage=100)
""" 通过脚本查询结果,返回一个结果元组 """
def MSsqlClient().select(sql[,param])->tuple
""" 通过脚本查询结果,将结果和字段名称组合成字典返回 """
def MSsqlClient().select_to_dict(sql[,param])->list(dict)
""" 执行增删改语句,返回影响行数 """
def MSsqlClient().execute(sql[,param])->int
""" 开启事务 """
def MSsqlClient().begin()->none
""" 结束事务,option为commin时提交,其他时回滚 """
def MSsqlClient().end(option='commit')->int
""" 执行语句,不提交,配合事务使用 """
def MSsqlClient().execute_without_commit(sql[,param])->int
```
#### 日志操作
日志使用的是国人开发的nb_log包,性能非常好,功能全面,就是使用起来会比loguru麻烦一些。
最方便的是可以实例化多个日志对象,将日志写入到不同的文件,可以根据项目,日志级别等来分日志。
封装如下:
- 隔离了一些配置,简化了使用。
- 定义了默认日志对象InnerLogger.log,写入文件为./log/base.log,日志级别在config里面定义
- 定义了flask内部api调用日志,写入文件为./log/invoke_api.log
```python
""" 使用示例 """
from plugin import log, get_log
log.info('xxx') # 使用默认日志对象记录日志
demo_log = get_log('demo','./log/demo.log',10)
demo_log.error('xxx') # 使用自定义日志对象记录日志
```
```python
""" 日志接口说明 """
"""
# 默认日志对象,各插件使用的日志对象,项目中可以直接使用这个对象记录日志,也可以自定义日志对象
"""
class InnerLogger.log
"""
# 生成一个日志对象,默认级别为error
@param
:module: 日志名称,根据不同的名称生成不同的日志对象,名称相同则返回的日志对象相同
:path: 完整的日志路径和名称
:level: 默认error
"""
def get_log(module, path, level)
```
#### 定时任务
使用的aspschduler包,BackgroundScheduler类。
支持普通定时任务和持久化定时任务。
##### 独立定时任务
+ 使用@SchedulerManager.Scheduler(Trigger),推荐
+ 使用SchedulerManager.add_job(func,Trigger)
```python
""" 使用示例 """
from plugin import SchedulerManager, Trigger
import time
@SchedulerManager.Scheduler(Trigger.interval(minutes=10)) # Trigger是自定义类,方便设定任务参数
def a():
print(time.time())
a() # 调用方法时会将方法加到定时器里面去,并默认当前执行一遍
```
```python
""" 接口定义 """
""" 作业任务管理,管理所有的定时任务 """
class SchedulerManager():
""" 任务配置 """
JOB_DEFAULTS_CONFIG = {
'coalesce': True, # 过时任务是否忽略
'replace_existing': True, # 如果存在相同id任务,是否替换
'max_instances': 10, # 最大实例数
# 'misfire_grace_time': 60 # 容错时间
}
"""
独立定时任务装饰器。
@param:
trigger: 触发器对象,需要是Trigger的子类(cron, interval, date)
max_instances: 最大实例数,默认为1,如果任务线程阻塞,将在下次执行。大于1时,将会安排其他线程执行
run_now: 是否立即运行,False时将在下一个周期开始运行
@return:
job_id: 作业id
"""
@staticmethod
def Scheduler(trigger, job_id=None, max_instances=1, run_now=True):
"""
添加一个独立的定时任务。
@param:
func: 定时任务函数
trigger: 触发器对象,需要是Trigger的子类(cron, interval, date)
job_id: 定时任务id,相同id的会被覆盖,如果不指定,则使用函数名+参数
max_instances: 最大实例数,默认为1,如果任务线程阻塞,将在下次执行。大于1时,将会安排其他线程执行
args: 函数参数
kwargs: 函数参数
@return:
job_id: 定时任务id
"""
@staticmethod
def add_job(func, args=None, kwargs=None, trigger=None, job_id=None, max_instances=1, run_now=True):
""" 停止指定独立任务 """
@staticmethod
def pause_job(job_id):
""" 恢复指定独立任务 """
@staticmethod
def resume_job(job_id):
""" 删除指定独立任务 """
@staticmethod
def remove_job(job_id):
```
##### 持久化定时任务
持久化定时任务会将任务记录到data.db。以防程序中断导致定时任务丢失。
搭配了flask_admin,支持界面动态配置任务参数以及任务内容。
#### 接口返回数据格式化
配合自定义错误,简化并统一接口的返回数据
```python
class MyResponse:
""" 设定接口返回的通用格式{code : xx, message : xx, data : xx} """
def __init__(self, module_log: logging.Logger = None) -> None:
""" 初始化时,可以定义记录日志的对象 """
def success(self, data=None):
"""
返回成功,只用设置data字段数据
需要实例化,调用方式MyResponse().success()
"""
def error(self, exception: MyException, message=None):
"""
返回失败,建议使用通用错误定义来返回,也可以使用元组,需要包含code和message
需要实例化,调用方式MyResponse(logger).error()
@param
:exception: 错误类型和描述,建议使用通用错误定义
:message: 自定义内容,记录错误日志用,方便调试
"""
@staticmethod
def Success(data):
""" 无需实例化,调用方式MyResponse.Success() """
@staticmethod
def Error(exception: MyException, message=None):
""" error的无需实例化版本,因为没有实例化,如果需要记录日志,只能记录到默认日志里面 """
```
#### 自定义异常类
定义了一些异常,用于下错误判断,日志记录和api返回。
```python
class MyException(Exception):
### req
ERROR_AUTH = (1, '无权限请求数据')
ERROR_REQ = (2, '无效的请求')
ERROR_REQ_PARAM = (3, '无效的请求,缺少关键参数')
ERROR_REQ_DATA = (4, '无效的请求,请求数据错误')
### program
ERROR_PROGRAMMA = (10, '程序错误')
ERROR_PLUGIN = (20, '插件程序错误')
### db
ERROR_DATABASE_CONN = (30, '数据库连接错误')
ERROR_DATABASE_WORK = (31, '数据库处理错误')
ERROR_DATABASE_EXEC = (32, '数据库执行错误')
### http
ERROR_UNKNOW = (500, '访问错误') # 网络访问的未知,不确定错误
```
#### 接口文档
使用的flask_docs包,重写了里面的一个方法。丰富了默认显示的文档内容。支持通过配置文件的配置设置文档内容。
使用示例
```python
@demo.route('/test')
@Create_Doc() # 这个注解是丰富接口文档,没有也会有简单的接口文档
def test():
return 'success'
### config.yml 配置
apidoc:
method:
test:
descr: 这是一个demo接口
args: |
-|-|-|- # 这里写请求参数,表题已经定义好了 name|必要|类型|说明
respargs: | # 返回字段
|-|-|-|-
```
#### TODO
- token_auth
- file_serve