zope3的json-rpc服务器和客户端实现
z3c.jsonrpc的Python项目详细描述
详细文档
jsonrpc
json是javascript对象表示法。JSON-RPC执行相同的服务 作为XML-RPC,除了传输是JSON而不是XML。
非常感谢Jim Washington在zif.jsonserver上所做的工作。此项目使用 吉姆写了很多代码。我实现了一个附加的python jsonrpc代理 可以与服务器通信。这意味着我们可以使用这个库调用 从python到python的json。json-rpc代理使用类似的模式,如 XML-RPC实现。
另外还有一个xmlhttp和json javascript实现 为javascript提供json-rpc代理实现。
此项目提供了建议的请求类型"application/json"。请求 类型"application/json rpc"是受支持的,只要它不是正式弃用的。
这个项目的目标是提供一个json-rpc实现。简单的 不支持使用browserrequest处理json调用的浏览器视图 通过这个包裹。我仍然不确定这是好是坏 我将带着这个包裹前往的方向。
我的一些目标是现在,但如果我能理解的话,将来可能会改变 关于json的所有概念,例如jspon、json p、crosssite等:
- 提供一种安全的方法来处理从客户端到服务器的json调用。 我希望有几天我们能实现jsonrequest。Crosssite似乎使用了 插话概念
- 简单的pythonic实现
- 与jquery一起使用(请参见http://www.jquery.org rel="nofollow">http://www.jquery.org)。
- 除jquery和基本zope包外,没有其他依赖项。
- 测试良好(javascript现在不是这种情况)
关于json
有关json的更多信息,请访问www.json.org。
有关详细信息,请参见http://json-rpc.org/wd/json-rpc-1-1-wd-20060807.html" rel="nofollow">http://json-rpc.org/wd/json-rpc-1-1-wd-20060807.html。 关于JSON 1.1规范。
此软件包不能执行的操作
json和这个包有不同的限制。这个包裹现在可以 不处理以下任务:
- 处理文件上载
- 处理GET请求
注意,jsonrpcrequest实现基于ihttprequest,这个 也就是说,如果您调用它们,就没有其他可用的浏览器页 python,例如getmultiadapter((context,request),name='myviewname')。这是 明确地这样做。如果你想使用这样的浏览器页面的内容 在json请求/调用中,您可以继承皮肤形式ijsonrpclayer和 ibrowserrequest并注册此自定义层的json-rpc视图。
json-rpc服务器
json服务器查找内容类型"application/json",并处理 请求为json-rpc。json的官方mime类型是"application/json" 旧的内容类型application/json rpc也受支持。
让我们定义一个内容对象:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)
并定义一个jsonrpc方法视图:
< Buff行情>>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')
让我们定义一个内容对象,它是一个容器:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""
>>> import persistent >>> from zope.container import btree
>>> class DemoContainer(btree.BTreeContainer): ... """Demo container.""" ... zope.interface.implements(IDemoContainer)
并定义一个jsonrpc方法视图:
< Buff行情>>>> from z3c.jsonrpc import publisher >>> class DemoContainerView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def available(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, foo, bar) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')
把它们放在假包装下 jsonsamples :
< Buff行情>>>> import sys >>> sys.modules['custom'] = type('Module', (), {})() >>> sys.modules['custom'].IDemoContent = IDemoContent >>> sys.modules['custom'].DemoContent = DemoContent >>> sys.modules['custom'].DemoView = DemoView >>> sys.modules['custom'].IDemoContainer = IDemoContainer >>> sys.modules['custom'].DemoContainer = DemoContainer >>> sys.modules['custom'].DemoContainerView = DemoContainerView
让我们演示如何注册jsonrpc视图:
< Buff行情>>>> from zope.configuration import xmlconfig >>> import z3c.jsonrpc >>> context = xmlconfig.file('meta.zcml', z3c.jsonrpc) >>> context = xmlconfig.string(""" ... <configure ... xmlns:z3c="http://namespaces.zope.org/z3c"> ... <z3c:jsonrpc ... for="custom.IDemoContent" ... class="custom.DemoView" ... permission="zope.Public" ... methods="hello greeting mixedparams kws showId forceValueError" ... layer="z3c.jsonrpc.testing.IJSONRPCTestSkin" ... /> ... </configure> ... """, context)
让我们演示如何为容器注册jsonrpc视图: (容器类也需要权限配置)
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""0
现在,我们将在站点中设置一个内容对象:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""1 现在我们可以从jsonrpc视图调用该方法: < Buff行情>
>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""2
但这不是直觉。让我们看看如何遍历方法 hello 使用遍历器:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""3
现在我们尝试使用测试浏览器访问json-rpc视图方法。正如你所能 看,没有可访问的视图。这是因为jsonrpc视图不是 浏览器视图,不可遍历。错误表明请求工厂 返回到浏览器请求工厂:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""4
测试
如果需要测试jsonrpc视图,可以使用如下所示的测试代理 下面的json-rpc代理部分。
json-rpc代理
json rpc包还提供了一个json-rpc代理实现。这个 实现类似于xmlrpclib中已知的实现,只是它可以 处理JSON而不是XML。
让我们尝试调用之前定义的方法 < Buff行情>
>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""5
根据jsonrpc规范中的定义,还允许省略参数 我们需要直接用一个帖子来测试,因为 测试代理总是设置参数。
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""6
>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""7
现在,让我们使用参数进行远程过程调用:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""8
让我们调用命名参数:
< Buff行情>>>> import zope.interface >>> class IDemoContent(zope.interface.Interface): ... """Demo content interface."""9
json响应中还有一个 id 。让我们使用这样一个json请求id 在我们的jsonrpcproxy中:
< Buff行情>>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)0
代理也知道这个id为jsonid:
< Buff行情>>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)1
json-rpc版本
让我们从版本1.0开始测试不同的json-rpc版本:
< Buff行情>>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)2
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)3
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)4
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)5
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)6
现在使用json-rpc 1.1版进行测试:
< Buff行情>>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)7
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)8
>>> import persistent >>> class DemoContent(persistent.Persistent): ... """Demo content.""" ... zope.interface.implements(IDemoContent)9
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')0
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')1
现在使用json-rpc 2.0版进行测试:
< Buff行情>>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')2
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')3
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')4
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')5
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')6
混合参数
注意,关键字参数将存储在request.form中。重要的 要知道的是,json-rpc不支持 一个方法调用。
< Buff行情>>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')7
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')8
>>> from z3c.jsonrpc import publisher >>> class DemoView(publisher.MethodPublisher): ... """Sample JSON view.""" ... ... def hello(self): ... return u"Hello World" ... ... def greeting(self, name): ... return u"Hello %s" % name ... ... def mixedparams(self, prefix, bar=None, foo=None): ... # Note; keyword arguments can be found in request.form ... return u"%s %s %s" % (prefix, bar, foo) ... ... def kws(self, adam=None, foo=None, bar=None): ... # Note; keyword arguments can be found in request.form ... a = self.request.get('adam') ... b = self.request.form.get('foo') ... c = self.request.form.get('bar') ... return u"%s %s %s" % (a, b, c) ... ... def showId(self): ... return u"The json id is: %s" % self.request.jsonId ... ... def forceValueError(self): ... raise ValueError('Something was wrong in server method.')9
错误处理
看看如果服务器引发异常会发生什么。我们会得到回应的 附加错误内容的错误:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""0
错误内容如下:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""1
下一次成功调用时将重置error属性:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""2
现在我们用一个伪造的jsonreader强制一个响应者。但首先我们 需要更换ijsonreader实用程序:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""3
同时设置站点挂钩:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""4
只需调用一个方法,这将引发一个响应错误:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""5
错误消息也存储在代理中:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""6
运输
我们在这里使用jsonrpctestproxy进行测试。这个json-rpc代理是一个包装器 对于原始的jsonrpcproxy,添加了handleerrors支持和一个特殊的 使用测试调用方的传输层。你可以用不同的 在实际用例中z3c.json.transport模块中定义的传输层 以及默认的jsonrpcproxy实现。
清理
现在我们需要清理自定义模块。
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""7
指令
jsonrpc指令
演示如何使用jsonrpc指令。注册的元配置 T指令。
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""8
现在在 z3c:jsonrpc中注册测试模块中定义的视图 指令:
< Buff行情>>>> import zope.interface >>> class IDemoContainer(zope.container.interfaces.IReadContainer): ... """Demo container interface."""9
让我们检查该视图是否注册为适配器:
< Buff行情>>>> import persistent >>> from zope.container import btree0
我们也可以使用层接口,它将限制我们的视图注册 特定的请求类型。提供这样的请求类型层:
< Buff行情>>>> import persistent >>> from zope.container import btree1
并注册一个新的json-rpc视图:
< Buff行情>>>> import persistent >>> from zope.container import btree2
设置新的内容存根:
< Buff行情>>>> import persistent >>> from zope.container import btree3
并测试新层中的视图:
< Buff行情>>>> import persistent >>> from zope.container import btree4
注意,对象B不知道默认请求层中的视图:
< Buff行情>>>> import persistent >>> from zope.container import btree5
setDefaultJsonRpcskin
< Buff行情>>>> import persistent >>> from zope.container import btree6
>>> import persistent >>> from zope.container import btree7
在设置默认请求之前,我们尝试为 要求:
< Buff行情>>>> import persistent >>> from zope.container import btree8
我们的请求不应提供任何默认KIN,因为我们没有注册任何:
< Buff行情>>>> import persistent >>> from zope.container import btree9
现在我们注册一个默认皮肤:
< Buff行情>>>> class DemoContainer(btree.BTreeContainer): ... """Demo container.""" ... zope.interface.implements(IDemoContainer)0
我们可以从适配器注册表中查找默认皮肤:
< Buff行情>>>> class DemoContainer(btree.BTreeContainer): ... """Demo container.""" ... zope.interface.implements(IDemoContainer)1
因为我们有一个默认的皮肤实用程序注册为 请求,新的请求实例应提供默认皮肤:
< Buff行情>>>> class DemoContainer(btree.BTreeContainer): ... """Demo container.""" ... zope.interface.implements(IDemoContainer)2
我们可以通过查找皮肤类型获得应用的默认皮肤:
< Buff行情>>>> class DemoContainer(btree.BTreeContainer): ... """Demo container.""" ... zope.interface.implements(IDemoContainer)3
更改
0.7.2(2013-10-11)
- handleexception :提供人类可读的回溯
0.7.1(2012-11-27)
- 修复jsonrpctesttransport以包含请求完整主机。 直到现在它才吃掉了港口。
0.7.0(2012-03-25)
- 修复:为publisher.processinputs中的parseerror添加了缺少的异常导入
- 从python导入doctest
0.6.0(2010-01-27)
- 清除设置依赖项,调整ftesting.zcml
- 调整覆盖率报告设置
- 实现了与zopepublication一起工作的错误视图概念
- 为已知的zope和json-rpc错误实现了默认错误视图
- 在响应中使用directresult
- 删除了未经身份验证的错误视图。这不起作用,需要 客户端使用的Java脚本库支持的自定义概念 边
版本0.5.4(2009-04-07)
- 处理jsonrpc请求中的空参数和无现有参数
版本0.5.3(2009-03-10)
- 修复:在zope.publisher中反映皮肤查找更改。使用新的skinnable 概念。
- 修正:默认皮肤没有根据继承的概念应用 来自zope.publisher.browser实现,因为我们的json-rpc请求 不提供ibrowserrequest。添加了将应用给定 创建请求实例期间的idefaultskin。
版本0.5.2(2009-02-24)
- 为所有json-rpc版本添加了测试
- 功能:实现了defaultjsonrpcskin指令
- 特性:支持所有jsonrpc版本的非位置参数。有 现在,在处理所有受支持版本的方法参数方面没有区别。
- 修复:对于jsonrpc 1.1版:
- 成功时不能提供"error"属性
- 如果出现错误,不得提供"result"属性
版本0.5.1(2008-01-24)
- 改善元数据。
- bug:皮肤代码依赖于后来才发布的api 还原。
版本0.5.0(2008-01-21)
- 初始版本