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
# 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
默认情况下,/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:
接口代码:
from application import jsonrpc
@jsonrpc.method(name="Home.index")
def index(id):
return "hello world!id=%s" % id
postman, 发起请求:
因为当前我们的服务端项目安装在虚拟机里面,并且我们设置了虚拟机的网络连接模式为NAT,所以一般情况下,我们无法直接通过手机访问虚拟机。因此,我们需要配置一下。
出现页面则表示访问成功。