需求:
Hybrid App,Hybrid App就是混合APP,就是Native结合Web的混合开发,就是内部本质是Web网页,使用打包软件给它套一层原生APP的外壳。
通过APICloud+Flask
完成。
有关移动端知识:传送门
解决方式:
文章目录
1.移动端项目搭建
1.注册APICloud帐号
APICloud官网:https://www.apicloud.com
开发文档:https://docs.apicloud.com
2.下载APP开发编辑器
注册完成后官网下面有开发工具,点击下载即可
网址:https://www.apicloud.com/studio3
下载完成后,直接解压即可使用
3.下载APP开发调试工具
1)首先可以下载一个安卓模拟器(海马玩模拟器, 夜神模拟器, 雷电模拟器都行)
雷电模拟器:https://www.ldmnq.com/
夜神的官网: https://www.yeshen.com/
2)再APICloud官网,控制台->SDK下载->AppLoader
下载即可,真机可以扫码下载,模拟器需下载压缩包,解压后拖入模拟器进行安装。
4.新建一个项目
打开第二步安装的编辑器,点击新建项目(如果没有安装git,要先安装个git才能使用)
按提示填写项目相关选项,即可创建成功
5.模拟器调试
1)在编辑器中,查看真机同步IP和端口(右击项目的根目录->查看WIFI真机同步IP和端口)
2)在模拟器中,屏幕右侧有一个不大不小的白色圆点,点击输入上一步的IP和端口,点击连接,白色圆点变绿则说明连接成功。
3)在编辑器中,右击项目目录->WIFI同步->打开, 接着选择WIFI全量同步.即可看到页面同步效果。
6.真机调试
有两种方式:
1)通过USB进行连接;
1.确保当前手机中已经安装了APPLoader工具(步骤3)
2.右击项目的根目录->USB同步
2)手机和电脑的网络连接同一网段下(步骤和模拟器调试一样)
2.服务端项目搭建
该后端框架需要实现的基本功能:
1.通过配置文件加载配置
2.通过终端启动项目和完成数据迁移
3.连接数据库(mysql和redis)并将session存在redis中
4.日志模块
5.能注册多个蓝图(自定义终端命令)
6.基于Flask-JSONRPC提供RPC接口
2.1 创建项目的虚拟环境
新建项目目录mofangapi,并创建虚拟环境
mkvirtualenv mofang
安装开发中使用的依赖模块
pip install flask==0.12.4
pip install flask-redis
pip install flask-session
pip install flask-script
pip install flask-mysqldb
pip install flask-sqlalchemy
pip install flask-migrate
2.2 服务器框架搭建
2.2.1创建 manage.py
文件
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run()
manage.py中不能存放大量的开发代码, 在开发中应该体现的是一种分工精神,所以我们可以把flask中各种功能代码进行分类分文件存储.
创建项目目录结构:
项目根目录/
├── application/ # 项目主要逻辑代码保存目录
| ├── settings/ # 项目配置存储目录
│ │ ├ dev.py # 开发阶段的配置文件
│ │ ├ prod.py # 生产阶段的配置文件
| | ├ __init__.py # 项目公共配置文件
│ ├── __init__.py # 项目初始化文件
├── manage.py # 项目的终端管理脚本文件
2.2.2构建全局初始化函数
把引导整个项目启动的全局初始化代码,保存到application/__init__py
,代码:
from flask import Flask
def init_app():
"""全局初始化"""
app = Flask(__name__)
return app
manage.py,中调用初始化函数,创建app应用对象,代码:
from application import init_app
app = init_app()
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run()
2.2.3通过终端命令启动项目
applicatiion/__init__py
,代码:
from flask import Flask
from flask_script import Manager
manager = Manager()
def init_app():
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 初始化终端脚本工具
manager.app = app
return manager
manage.py,代码:
from application import init_app
manage = init_app()
@manage.app.route('/')
def index():
return 'index'
if __name__ == '__main__':
manage.run()
运营项目的方式就要修改成如下:
python manage.py runserver -h0.0.0.0 -p5000
2.2.4项目加载配置
编写项目默认配置文件, application/settings/__init__.py
代码:
class InitConfig():
"""项目默认初始化配置"""
DEBUG = True
开发环境配置application/settings/dev.py
,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
生产环境配置application/settings/prod.py
,代码:
from . import InitConfig
class Config(InitConfig):
"""项目运营环境下的配置"""
DEBUG = False
在application/utils/config.py中准备加载配置的函数代码:
from importlib import import_module
def load_config(config_path):
"""自动加载配置"""
module = import_module(config_path)
name = config_path.split(".")[-1]
if name == "settings":
return module.InitConfig
else:
return module.Config
在项目引导文件application/__init__py
中加载配置,代码:
from flask import Flask
from flask_script import Manager
from application.utils.config import load_config
manager = Manager()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 初始化终端脚本工具
manager.app = app
return manager
在创建app对象的项目启动文件manage.py中,设置配置(配置加载项路径)
from application import init_app
manage = init_app("application.settings.dev")
@manage.app.route('/')
def index():
return 'index'
if __name__ == '__main__':
manage.run()
2.2.5数据库初始化
1)SQLAlchemy初始化
默认项目配置文件中增加配置选项,application/settings/__init__.py
,代码:
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
开发配置dev.py
中,配置数据库连接信息,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
# 数据库
SQLALCHEMY_DATABASE_URI = "mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4"
SQLALCHEMY_ECHO = True
在mysql终端下, 创建数据库用户,命令如下:
create database mofang charset=utf8mb4;
# 针对当前数据库配置账户信息
create user mofang_user identified by 'mofang';
grant all privileges on mofang.* to 'mofang_user'@'%';
flush privileges;
在项目全局引导文件中,对数据库功能进行初始化,application/__init__.py
,代码:
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from application.utils.config import load_config
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
# 初始化终端脚本工具
manager.app = app
return manager
2)Redis数据库初始化
默认配置文件,application/settings/__init__py
,代码:
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
# Redis
REDIS_URL = "redis://@127.0.0.1:6379/0"
在全局引导文件中, 对redis进行初始化,applicaiton/__init__.py
,代码:
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from application.utils.config import load_config
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# 初始化终端脚本工具
manager.app = app
return manager
3)Session存储到redis数据库中
因为需要单独设置一个数据库存放session,所以我们再次单独配置一个关于session加载配置的函数init_session
,
application/utils/session.py
,代码:
from redis import Redis
def init_session(app):
host = app.config.get("SESSION_REDIS_HOST","127.0.0.1")
port = app.config.get("SESSION_REDIS_PORT",6379)
db = app.config.get("SESSION_REDIS_DB",0)
print(db)
app.config["SESSION_REDIS"] = Redis(host=host,port=port,db=db)
默认配置文件application/settings/__init__.py
中, 添加配置项:
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
# Redis
REDIS_URL = ""
# 设置密钥,可以通过 base64.b64encode(os.urandom(48)) 来生成一个指定长度的随机字符串
SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
# session存储配置
# session存储方式配置
SESSION_TYPE = "redis"
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = False
# 设置session_id在浏览器中的cookie有效期
PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,单位是秒
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = None # 用于连接redis的配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 0
在本地开发配置中,设置session存储指定的redis库中,application/settings/dev.py
,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
# 数据库
SQLALCHEMY_DATABASE_URI = "mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4"
SQLALCHEMY_ECHO = True
# redis
REDIS_URL = "redis://@127.0.0.1:6379/0"
# session存储配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
在项目全局引导文件application/__init__.py
中对session存储进行初始化,代码:
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from application.utils.config import load_config
from application.utils.session import init_session
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 初始化终端脚本工具
manager.app = app
return manager
4)数据迁移初始化
项目全局引导文件application/__init__py
,代码:
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from application.utils.config import load_config
from application.utils.session import init_session
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app,db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)
# 初始化终端脚本工具
manager.app = app
return manager
完成上面的项目构建步骤以后,此时目录结构如下:
项目根目录/
├── docs/ # 项目开发文档/接口等备份资料存储目录
├── logs/ # 项目日志存储目录
├── application/ # 项目主要逻辑代码保存目录
| ├── settings/ # 项目配置存储目录
│ │ ├ dev.py # 开发阶段的配置文件
│ │ ├ prod.py # 生产阶段的配置文件
| | ├ __init__.py # 项目公共配置文件
| ├── utils/ # 工具函数库/类库
│ │ ├ session.py # session相关的辅助函数
│ │ ├ config.py # 配置相关的辅助函数
│ ├── __init__.py # 项目初始化文件
└── manage.py # 项目的终端管理脚本文件
2.2.6日志初始化
flask中没有内置的日志功能,我们在使用的时候, 一般日志如果不是核心重点,则通过由python内置的logging模块进行配置集成使用即可, 如果项目中日志发挥作用比较重要, 则一般安装部署 ELK日志分析系统.
1)日志的等级
FATAL/CRITICAL = 致命的,危险的
ERROR = 错误
WARNING = 警告
INFO = 信息
DEBUG = 调试
2)构建日志模块
把日志初始化相关的代码封装成一个函数,application/utils/logger.py
,代码:
import logging
from logging.handlers import RotatingFileHandler
class Log():
"""日志模块"""
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self,app):
self.app = app
return self.setup()
def setup(self):
"""安装日志功能到flask中"""
# 设置日志的记录等级
logging.basicConfig(level=self.app.config.get("LOG_LEVEL")) # 调试debug级
# 创建日志记录器,指明日志保存的路径、每个日志文件的最大大小、保存的日志文件个数上限
file_log_handler = RotatingFileHandler(
self.app.BASE_DIR+self.app.config.get("LOG_DIR"),
maxBytes=self.app.config.get("LOG_MAX_BYTES"),
backupCount=self.app.config.get("LOG_BACKPU_COUNT")
)
# 创建日志记录的格式 日志等级 输入日志信息的文件名 行数 日志信息
formatter = logging.Formatter('%(name)s: %(levelname)s %(asctime)s %(filename)s:%(lineno)d %(message)s')
# 为刚创建的日志记录器设置日志记录格式
file_log_handler.setFormatter(formatter)
# 为全局的日志工具对象(flaskapp使用的)添加日志记录器
logging.getLogger(self.app.config.get("LOG_NAME")).addHandler(file_log_handler)
# 返回日志器对象提供给业务开发
logger = logging.getLogger(self.app.config.get("LOG_NAME"))
return logger
application/settings/__init__.py
代码:
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
# Redis
REDIS_URL = ""
# 设置密钥,可以通过 base64.b64encode(os.urandom(48)) 来生成一个指定长度的随机字符串
SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
# session存储配置
# session存储方式配置
SESSION_TYPE = "redis"
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = False
# 设置session_id在浏览器中的cookie有效期
PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,单位是秒
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = None # 用于连接redis的配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# 日志相关配置
LOG_LEVEL = "INFO" # 日志输出到文件中的最低等级
LOG_DIR = "logs/0.log" # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = "flask" # 日志器的名字
开发环境配置文件dev.py
中配置具体的日志相关信息,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
# 数据库
SQLALCHEMY_DATABASE_URI = "mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4"
SQLALCHEMY_ECHO = True
# redis
REDIS_URL = "redis://@127.0.0.1:6379/0"
# session存储配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 日志配置
LOG_LEVEL = "DEBUG" # 日志输出到文件中的最低等级
LOG_DIR = "/logs/mofang.log" # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = "mofang" # 日志器名称
在 application/__init__.py
文件中的init_app
方法中调用日志初始化。
import os,logging
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
# 日志对象
log = Log()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app,db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)
# 日志初始化
app.log = log.init_app(app)
# 初始化终端脚本工具
manager.app = app
return manager
新增日志以后的项目目录结构
.
├── application
│ ├── __init__.py
│ ├── settings
│ │ ├── dev.py
│ │ ├── __init__.py
│ │ ├── prod.py
│ └── utils
│ ├── config.py
│ ├── __init__.py
│ ├── logger.py # 日志相关模块代码库
│ └── session.py
├── docs
├── logs # 日志文件存储目录
│ └── mofang.log
└── manage.py
经过上面的改造,我们接下来就可以开始创建蓝图了。
2.2.7蓝图的初始化
1)自定义终端命令自动生成蓝图
在application下创建apps目录,apps以后专门用于保存每一个项目的蓝图,并在apps创建home蓝图目录,并在__init__.py
文件中创建蓝图对象
通过自定义终端命令, 创建一个自动生成蓝图目录的命令.application/utils/commands.py
,代码:
from flask_script import Command, Option
class BlueprintCommand(Command):
"""蓝图生成命令"""
name = "blue"
option_list = [
Option('--name', '-n', dest='name'),
]
def run(self, name):
# 生成蓝图名称对象的目录
os.mkdir(name)
open("%s/__init__.py" % name, "w")
open("%s/views.py" % name, "w")
open("%s/models.py" % name, "w")
with open("%s/urls.py" % name, "w") as f:
content = """from . import views
from application.utils import path
urlpatterns = [
]"""
f.write(content)
print("蓝图%s创建完成...." % name)
上面的命令就可以帮我们完成项目中生成蓝图的功能,就下来我们就可以直接把命令注册到manage对象中就可以使用了.
但是, 我们往后的开发中肯定还会继续的需要进行自定义终端命令,所以我们声明一个load_command的函数,让自动帮我们完成加载注册自定义终端命令的过程.
application/utils/commands.py
,代码:
import os
from importlib import import_module
from flask_script import Command, Option
import inspect
def load_command(manager,command_path=None):
"""自动加载自定义终端命令"""
if command_path is None:
command_path = "application.utils.commands"
module = import_module(command_path)
class_list = inspect.getmembers(module,inspect.isclass)
for class_item in class_list:
if issubclass(class_item[1],Command) and class_item[0] != "Command":
manager.add_command(class_item[1].name,class_item[1])
class BlueprintCommand(Command):
"""蓝图生成命令"""
name = "blue"
option_list = [
Option('--name', '-n', dest='name'),
]
def run(self, name):
# 生成蓝图名称对象的目录
os.mkdir(name)
open("%s/__init__.py" % name, "w")
open("%s/views.py" % name, "w")
open("%s/models.py" % name, "w")
with open("%s/urls.py" % name, "w") as f:
content = """from . import views
from application.utils import path
urlpatterns = [
]"""
f.write(content)
print("蓝图%s创建完成...." % name)
在项目全局引导文件application/__init__.py
中, 调用load_command函数注册命令
import os,logging
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
from application.utils.commands import load_command
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
# 日志对象
log = Log()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app,db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)
# 日志初始化
app.log = log.init_app(app)
# 初始化终端脚本工具
manager.app = app
# 注册自定义命令
load_command(manager)
return manager
接下来就可以在终端下,通过命令生成蓝图目录了.
命令:
cd application/apps
python ../../manage.py blue -nhome
2)注册蓝图
有了蓝图以后,接下来我们就可以视图代码,模型代码,路由代码等存储到蓝图目录下了,但是我们需要把蓝图注册到app应用对象下.所以我们注册蓝图这块代码也可以封装到一个函数中.让程序自动识别并注册.
项目中可以有多个蓝图,但是有些蓝图可能并不能提供给客户端访问,所以我们需要在配置文件中声明一个蓝图注册列表, 在init_blueprint
函数中只注册配置列表的蓝图
application/settings/__init__.py
,代码:
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
# Redis
REDIS_URL = ""
# 设置密钥,可以通过 base64.b64encode(os.urandom(48)) 来生成一个指定长度的随机字符串
SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
# session存储配置
# session存储方式配置
SESSION_TYPE = "redis"
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = False
# 设置session_id在浏览器中的cookie有效期
PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,单位是秒
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = None # 用于连接redis的配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# 日志相关配置
LOG_LEVEL = "INFO" # 日志输出到文件中的最低等级
LOG_DIR = "logs/0.log" # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = "flask" # 日志器的名字
# 蓝图注册列表
INSTALLED_APPS = [
]
application/settings/dev.py
,代码:
from . import InitConfig
class Config(InitConfig):
"""项目开发环境下的配置"""
DEBUG = True
# 数据库
SQLALCHEMY_DATABASE_URI = "mysql://mofang_user:mofang@127.0.0.1:3306/mofang?charset=utf8mb4"
SQLALCHEMY_ECHO = True
# redis
REDIS_URL = "redis://@127.0.0.1:6379/0"
# session存储配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 日志配置
LOG_LEVEL = "DEBUG" # 日志输出到文件中的最低等级
LOG_DIR = "/logs/mofang.log" # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = "mofang" # 日志器名称
# 注册蓝图
INSTALLED_APPS = [
"application.apps.home",
]
在init_blueprint函数中,针对注册的蓝图列表注册到app应用对象里面,
application/utils/__init__.py
,代码:
def init_blueprint(app):
"""自动注册蓝图"""
blueprint_path_list = app.config.get("INSTALLED_APPS")
for blueprint_path in blueprint_path_list:
blueprint_name = blueprint_path.split(".")[-1]
# 自动创建蓝图对象
blueprint = Blueprint(blueprint_name,blueprint_path)
# 注册蓝图对象到app应用对象中
app.register_blueprint(blueprint,url_prefix="")
项目全局引导文件中, 调用init_blueprint
方法, 自动注册蓝图.application/__init__.py
,代码:
import os,logging
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from application.utils import init_blueprint
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
from application.utils.commands import load_command
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
# 日志对象
log = Log()
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app,db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)
# 日志初始化
app.log = log.init_app(app)
# 蓝图注册
init_blueprint(app)
# 初始化终端脚本工具
manager.app = app
# 注册自定义命令
load_command(manager)
return manager
3)注册蓝图下的视图和对应的路由
注册了蓝图对象以后,蓝图下面的视图方法和视图对应的路由关系也要进行注册!
所以, 在蓝图home下面的urls.py文件中,通过path方法把url地址和视图方法进行处理成字典,然后把字典作为成员返回到变量urlpatterns列表中.
application/utils/__init__.py
,生成path函数,代码:
from flask import Blueprint
from importlib import import_module
def path(rule,func_view):
# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
return {"rule":rule,"view_func":func_view}
在蓝图下的urls.py中,注册视图和路由的关系,home.urls.py
,代码:
from . import views
from application.utils import path
urlpatterns = [
path("/",views.index),
]
视图代码:
def index():
return 'index'
在init_blueprint初始化蓝图的函数中, 生成蓝图对象以后自动加载并注册蓝图和视图
application/utils/__init__.py
,代码:
from flask import Blueprint
from importlib import import_module
def path(rule,func_view):
# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
return {"rule":rule,"view_func":func_view}
def init_blueprint(app):
"""自动注册蓝图"""
blueprint_path_list = app.config.get("INSTALLED_APPS")
for blueprint_path in blueprint_path_list:
blueprint_name = blueprint_path.split(".")[-1]
# 自动创建蓝图对象
blueprint = Blueprint(blueprint_name,blueprint_path)
# 蓝图自动注册和绑定视图和子路由
url_module = import_module(blueprint_path+".urls") # 加载蓝图下的子路由文件
for url in url_module.urlpatterns: # 遍历子路由中的所有路由关系
blueprint.add_url_rule(**url) # 注册到蓝图下
# 注册蓝图对象到app应用对象中
app.register_blueprint(blueprint,url_prefix="")
此时, 运行项目,就允许蓝图中视图通过url地址提供给客户端访问了.
4)设置url_prefix路由前缀(总路由)
上面蓝图注册到app时, 没有设置url_prefix路由前缀, 接下来我们可以单独设置一个总路由application/urls.py
,进行路由前缀的设置.
在项目默认配置文件中,application/settings/__init__.py
,新增总路由的配置项
class InitConfig():
"""项目默认初始化配置"""
# 调试模式
DEBUG = True
# 数据库相关配置
SQLALCHEMY_DATABASE_URI = ""
# 动态追踪修改设置
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 查询时会显示原始SQL语句
SQLALCHEMY_ECHO= True
# Redis
REDIS_URL = ""
# 设置密钥,可以通过 base64.b64encode(os.urandom(48)) 来生成一个指定长度的随机字符串
SECRET_KEY = "y58Rsqzmts6VCBRHes1Sf2DHdGJaGqPMi6GYpBS4CKyCdi42KLSs9TQVTauZMLMw"
# session存储配置
# session存储方式配置
SESSION_TYPE = "redis"
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = False
# 设置session_id在浏览器中的cookie有效期
PERMANENT_SESSION_LIFETIME = 24 * 60 * 60 # session 的有效期,单位是秒
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = None # 用于连接redis的配置
SESSION_REDIS_HOST = "127.0.0.1"
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 1
# 调整json数据转换中文的配置
JSON_AS_ASCII=False
# 日志相关配置
LOG_LEVEL = "INFO" # 日志输出到文件中的最低等级
LOG_DIR = "logs/0.log" # 日志存储目录
LOG_MAX_BYTES = 300 * 1024 * 1024 # 单个日志文件的存储上限[单位: b]
LOG_BACKPU_COUNT = 20 # 日志文件的最大备份数量
LOG_NAME = "flask" # 日志器的名字
# 蓝图注册列表
INSTALLED_APPS = [
]
# 总路由
URL_PATH = "application.urls"
创建总路由文件并注册蓝图和路由前缀的关系, application/urls.py
,代码:
from application.utils import include
urlpatterns = [
include("","home.urls"),
]
接下来,在init_blueprint
蓝图初始化函数中新增判断识别路由前缀的代码; 同时, 把路由前缀和蓝图映射关系的处理代码封装成include
方法,方便以后需求变化时可以直接调整.
from flask import Blueprint
from importlib import import_module
def path(rule,func_view):
# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
return {"rule":rule,"view_func":func_view}
def include(url_prefix, blueprint_path):
"""把路由前缀和蓝图进行关系映射"""
return {"url_prefix":url_prefix,"blueprint_path":blueprint_path}
def init_blueprint(app):
"""自动注册蓝图"""
blueprint_path_list = app.config.get("INSTALLED_APPS")
for blueprint_path in blueprint_path_list:
blueprint_name = blueprint_path.split(".")[-1]
# 自动创建蓝图对象
blueprint = Blueprint(blueprint_name,blueprint_path)
# 蓝图自动注册和绑定视图和子路由
url_module = import_module(blueprint_path+".urls") # 加载蓝图下的子路由文件
for url in url_module.urlpatterns: # 遍历子路由中的所有路由关系
blueprint.add_url_rule(**url) # 注册到蓝图下
# 读取总路由文件
url_path = app.config.get("URL_PATH")
urlpatterns = import_module(url_path).urlpatterns # 加载蓝图下的子路由文件
url_prefix = "" # 蓝图路由前缀
for urlpattern in urlpatterns:
if urlpattern["blueprint_path"] == blueprint_name+".urls":
url_prefix = urlpattern["url_prefix"]
break
# 注册蓝图对象到app应用对象中, url_prefix 蓝图的路由前缀
app.register_blueprint(blueprint,url_prefix=url_prefix)
5)注册蓝图模型
在蓝图下的models.py中声明模型,例如:
from application import db
class User(db.Model):
__tablename__ = "mf_user"
id = db.Column(db.Integer, primary_key=True, comment="主键ID")
name = db.Column(db.String(255), unique=True, comment="账户名")
password = db.Column(db.String(255), comment="登录密码")
ip_address = db.Column(db.String(255), index=True, comment="登录IP")
def __repr__(self):
return self.name
然后在终端下执行数据迁移
cd ../..
python manage.py db init
python manage.py db migrate -m "text"
上面的命令执行以后, 我们可以发现模型根本被flask进行识别到.所以我们需要把模型注册到flask项目中.
application/utils/__init__.py
,代码:
from flask import Blueprint
from importlib import import_module
def path(rule,func_view):
# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
return {"rule":rule,"view_func":func_view}
def include(url_prefix, blueprint_path):
"""把路由前缀和蓝图进行关系映射"""
return {"url_prefix":url_prefix,"blueprint_path":blueprint_path}
def init_blueprint(app):
"""自动注册蓝图"""
blueprint_path_list = app.config.get("INSTALLED_APPS")
for blueprint_path in blueprint_path_list:
blueprint_name = blueprint_path.split(".")[-1]
# 自动创建蓝图对象
blueprint = Blueprint(blueprint_name,blueprint_path)
# 蓝图自动注册和绑定视图和子路由
url_module = import_module(blueprint_path+".urls") # 加载蓝图下的子路由文件
for url in url_module.urlpatterns: # 遍历子路由中的所有路由关系
blueprint.add_url_rule(**url) # 注册到蓝图下
# 读取总路由文件
url_path = app.config.get("URL_PATH")
urlpatterns = import_module(url_path).urlpatterns # 加载蓝图下的子路由文件
url_prefix = "" # 蓝图路由前缀
for urlpattern in urlpatterns:
if urlpattern["blueprint_path"] == blueprint_name+".urls":
url_prefix = urlpattern["url_prefix"]
break
# 注册模型
import_module(blueprint_path+".models")
# 注册蓝图对象到app应用对象中, url_prefix 蓝图的路由前缀
app.register_blueprint(blueprint,url_prefix=url_prefix)
项目能自动加载总路由和蓝图路由以后的项目目录结构,如下:
项目根目录/
├── application/ # 项目主要逻辑代码保存目录
| ├── settings/ # 项目配置存储目录
│ │ ├ __init__.py # 项目默认初始化配置文件
│ │ ├ dev.py # 开发阶段的配置文件
│ │ └ prod.py # 生产阶段的配置文件
│ ├── __init__.py # 项目初始化全局引导文件
| ├── utils/ # 项目工具类库目录
│ │ ├ commands.py # 自定义命令和加载命令的相关函数
│ │ ├ config.py # 项目配置加载的辅助函数
│ │ ├ session.py # 项目存储session相关的函数
│ │ └ logger.py # 日志模块
│ ├── apps/ # 保存项目中所有蓝图的存储目录
│ │ ├── home # 蓝图目录【这里是举例而已】
│ │ │ ├── __init__.py # 蓝图的初始化文件
│ │ │ ├── urls.py # 蓝图的子路由文件
│ │ │ ├── models.py # 蓝图的模型文件
│ │ │ └── views.py # 蓝图的视图文件
│ │ ├── __init__.py
│ └── urls.py # 总路由
├── manage.py # 项目的终端管理脚本文件
2.2.8 服务端基于Flask-JSONRPC提供RPC接口
JSON-RPC是一个无状态的、轻量级的远程过程调用(RPC)协议。
所谓的RPC,Remote Procedure Call
的简写,中文译作远程过程调用或者远程服务调用。
直观的理解就是,通过网络请求远程服务,获取指定接口的数据,而不用知晓底层网络协议的细节。
RPC
支持的格式很多,比如XML
格式,JSON
格式等等。最常用的肯定是json-rpc。
JSON-RPC协议中的客户端一般是为了向远程服务器请求执行某个方法/函数。客户端向实现了JSON-RPC协议的服务端发送请求,多个输入参数能够通过数组或者对象传递到远程方法,这个远程方法也能返回多个输出数据,具体是什么,当然要看具体的方法实现。因为RPC可以通俗理解为:
客户端请求服务端完成某一个服务行为,所以RPC规范要求: 客户端发送的所有请求都是POST请求!!!
所有的传输数据都是单个对象,用JSON格式进行序列化。
请求要求包含三个特定属性:
jsonrpc: 用来声明JSON-RPC协议的版本,现在基本固定为“2.0”
method,方法,是等待调用的远程方法名,字符串类型
params,参数,对象类型或者是数组,向远程方法传递的多个参数值
id,任意类型值,用于和最后的响应进行匹配,也就是这里设定多少,后面响应里这个值也设定为相同的
响应的接收者必须能够给出所有请求以正确的响应。这个值一般不能为Null,且为数字时不能有小数。
响应也有三个属性:
jsonrpc, 用来声明JSON-RPC协议的版本,现在基本固定为“2.0”
result,结果,是方法的返回值,调用方法出现错误时,必须不包含该成员。
error,错误,当出现错误时,返回一个特定的错误编码,如果没有错误产生,必须不包含该成员。
id,就是请求带的那个id值,必须与请求对象中的id成员的值相同。请求对象中的id时发生错误(如:转换错误或无效的请求),它必须为Null
当然,有一些场景下,是不用返回值的,比如只对客户端进行通知,由于不用对请求的id进行匹配,所以这个id就是不必要的,置空或者直接不要了。
在flask中要实现提供json-rpc接口,开发中一般使用Flask JSON-RPC模块来实现。
git地址:https://github.com/cenobites/flask-jsonrpc
文档:http://wiki.geekdream.com/Specification/json-rpc_2.0.html
1) 安装Flask-JSONRPC模块
# pip install Flask-JSONRPC # 如果出现问题,则降低版本到0.3.1
pip install Flask-JSONRPC==0.3.1
快速实现一个测试的RPC接口。
例如,我们直接在application/__init__.py
项目初始化文件中进行初始化jsonrpc并关闭csrf防范机制,代码。
import os,logging
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from flask_jsonrpc import JSONRPC
from application.utils import init_blueprint
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
from application.utils.commands import load_command
# 创建终端脚本管理对象
manager = Manager()
# 创建数据库链接对象
db = SQLAlchemy()
# redis链接对象
redis = FlaskRedis()
# Session存储对象
session_store = Session()
# 数据迁移实例对象
migrate = Migrate()
# 日志对象
log = Log()
# 初始化jsonrpc模块
jsonrpc = JSONRPC(service_url='/api')
def init_app(config_path):
"""全局初始化"""
# 创建app应用对象
app = Flask(__name__)
# 项目根目录
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 加载配置
Config = load_config(config_path)
app.config.from_object(Config)
# 数据库初始化
db.init_app(app)
redis.init_app(app)
# session存储初始化
init_session(app)
session_store.init_app(app)
# 数据迁移初始化
migrate.init_app(app,db)
# 添加数据迁移的命令到终端脚本工具中
manager.add_command('db', MigrateCommand)
# 日志初始化
app.log = log.init_app(app)
# 蓝图注册
init_blueprint(app)
# 初始化json-rpc
jsonrpc.init_app(app)
# 初始化终端脚本工具
manager.app = app
# 注册自定义命令
load_command(manager)
return manager
application/apps/home/views.py
,编写接口代码:
# 实现rpc接口
from application import jsonrpc
@jsonrpc.method(name="Home.index")
def index():
return "hello world!"
客户端需要发起post请求,访问地址为:http://服务器地址:端口/api
2) 注意
默认情况下,/api
接口只能通过post请求访问。如果要使用jsonrpc提供的界面调试工具,则访问地址为:
http://服务器地址端口/api/browse/
可以通过postman发送请求接口,访问数据格式应是:
请求地址:http://127.0.0.1:5000/api
请求体:
{
"jsonrpc": "2.0",
"method": "Home.index",
"params": {},
"id": "1"
}
错误提示:如果访问浏览器页面空白,终端显示如下:
则是因为Flask版本与 Werkzeug 组件版本不兼容的问题导致。
解决方案1,降低Werkzeug版本到0.16以下版本:
pip uninstall Werkzeug
pip install Werkzeug==0.16.1
解决方案2,修改flask/json.py文件中源码的判断条件,把is_xhr
改成is_json
:
if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_json:
3) 基于api接口接受来自客户端的参数
接口代码:
from application import jsonrpc
@jsonrpc.method(name="Home.index")
def index(id):
return "hello world!id=%s" % id
基于以上步骤项目的框架基本完成。