2021-06-29

Flasky-API

This framework refer to the source code examples for the second edition of O'Reilly book Flask Web Development.

The commits and tags in this repository are carefully created to match every upgrade and launch of the framework. Please read the section titled "How to Work with the Example Code" in the foreword for instructions.

License

© Contributors, 2021. Licensed under xueersi license.

Flask Development

Developing Web Applications with Python
Take full creative control of your web applications with Flask, the Python-based microframework.

Depends

package version
python 3.6.5
Flask
requests
redis
numpy
pandas
scipy
matplotlib
gunicorn
tensorflow
gensim
jieba
Keras
scikit-learn

Introduction to the framework

/app 应用核心源码

/config 配置文件

/doc 文档/数据汇总

/scripts 脚本文件汇总

/test 单元测试

/venv 虚拟环境

DockerFile docker容器脚本

Makefile 机器初始化脚本

requirements.txt 依赖汇总

run.py 启动入口

  • /app

/app/api 注册api和blueprint,业务源码

/app/auth 装饰器模块

/app/db 数据库模块

/app/depend 外部依赖

/app/log 日志模块

/app/modules 算法/数据结构模块

/app/utils 工具模块

/app/__ init__.py app初始化

/app/exceptions.py 异常定义

How to Work with the Example Code

Community |
Documentation |
Resources |
Version Notes

  • profile

框架级别配置

config下的env_*.json
区分测试/灰度/线上环境

from flask import current_app
db_conf_dict = current_app.config["DB_CONF"]
db_conf_dict = current_app.config["APP_NAME"]

接口级别配置

在config/interface路径下,新建*.json文件

from flask import current_app
if_conf_dict = current_app.config["去除后缀的文件名"]
  • register api

/app/api/路径下,每个文件夹隔离业务,一个或多个蓝图来管理api资源
views.py中声明api的处理class类

class Interact(Resource):

  def post(self):
      """post request"""
      response = dict()
      parser = reqparse.RequestParser()
      parser.add_argument('aa', type=str, required=True)
      try:
          pass
      except Exception as err:
          pass
      finally:
          pass
      return jsonify(response)

init.py中生成蓝图,添加api资源

from flask import Blueprint
from flask_restful import Api
from views import Interact, Create
  
new_bp = Blueprint('session', __name__,
                  template_folder='templates',
                  static_folder='static')
API = Api(new_bp)
# API.add_resource(Create, '/create', endpoint='create')
API.add_resource(Interact, '/interact', endpoint='interact')
  • create app

/app/init.py中通过上下文环境导入蓝图,并注册入app

# ------------------------------------
from .api_v1 import new_bp as demo
app.register_blueprint(demo, url_prefix='/api/v1/demo')
# ------------------------------------
# Add different versions of blueprints
# ------------------------------------
  • run test

启动run.py运行测试


加载框架级别和接口级别配置,可以理解为:把json文件中的键值对整理成有嵌套关系的字典,挂在flask实例的config属性下。

{  
    "dsp_redis": {
        "host": "10.90.72.116",
        "port": 12921,
        "db": 0
    }
}
db_conf_dict = current_app.config["DB_CONF"]  # 拿到这个字典对象
db_conf_dict["dsp_redis"]["port"]  # 拿到12921这个端口值
# 文件名:/config/interface/fenban.json
{  
    "normal_user_code":  0,
    "high_user_code":  297,
    "mid_user_code":  298,
    "low_user_code":  299
}
# 获取分班配置对象
fenban_conf_dict = current_app.config["FENBAN"]
fenban_conf_dict.get("mid_user_code")

初始化日志
读取并设置日志存放路径
设置日志等级
Handler设置
输出格式设置
创建logger对象,run_logger/error_logger/access_logger
其中前两者是logging模块自带的,输出一个字符串,后面的access_logger是自定义的logger,可以认为是一个字典,在一个请求的生命周期内,可以通过access_logger设置字典中的键值对,以及嵌套的兼职对,一个请求的生命周期结束后,会将该access_logger(字典)序列化成字符串,持久化到access.log文件中

from app.log import get_logger

run_logger = get_logger('run_logger')   # 运行日志,任意字符串
run_logger.info("这里是info日志")

error_logger = get_logger('error_logger')   # 错误日志,异常信息
error_logger.error("这里是error日志")

access_logger = get_logger('access_logger')    # access 日志,json日志
access_logger.set_root_value("url", "/api/copy/copy")
ai_res = "请求ai中台返回的结果"
access_logger.set_value("ai_response", ai_res)
# access.log文件中: {"url": "/api/copy/copy", "context": {"ai_response": "请求ai中台返回的结果"}}

初始化数据库(以redis为例)
通过读取配置的方式all_db_conf = current_app.config['DB_CONF']拿到所有数据库的配置,例如:

{
    "dsp_redis": {
        "host": "10.90.72.116",
        "port": 12921,
        "db": 0,
        "auth": "******"
    },
    "dmp_redis": {
        "host": "10.90.72.116",
        "port": 13321,
        "db": 0,
        "auth": "******"
    },
    ...
    {...}
}

遍历all_db_conf下的key,每个key会创建一个db实例,并挂到current_app下,相当于current_app的一个属性。
db_type为redis_db,所以通过redis模块创建redis实例,并将该redis实例挂到current_app下面setattr(current_app, "dsp_redis", redis_ins),使用的时候通过getattr(current_app, "dsp_redis")获取redis实例,我们将getattr方法封装到get_db函数中,方便大家使用。

from app.db import get_db
redis_ins = get_db("dsp_redis")  # 相当于getattr(current_app, "dsp_redis")
redis_conn = redis_ins.connect()
redis_conn.set("1", 1)
redis_conn.get("1")
  • run test
    使用unittest模块,文件名以test开头,定义一个类继承(unittest.TestCase),测试函数以test开头。
    快速实现一个测试用例:
    1.复制一份/test/test_dsp/test_demo.py到/test/test_dsp/目录下,例如:/test/test_dsp/test_copy.py
    2.修改一下路由(url),在register api那里第三步的init文件中
    3.设置自己接口的请求参数。
    4.编写对结果的预判和断言
    def test_posts(self):   # post 测试用例
        url = '/api/copy/copy'  # 路由
        data = {'user_id': 100001}  # 请求参数
        response = self.client.post(  # 发送请求
            url,
            headers=get_api_headers(),
            data=json.dumps(data)
        )
        rtn = response.get_json()
        print("test_de1mo.py", rtn)
        self.assertEqual(rtn['data']['user_type'], 'normal')  # 结果预判和断言

你可能感兴趣的:(2021-06-29)