在dojo中使用JSON-RPC_jsonicrpcresult serviceinvoker-程序员宅基地

技术标签: button  function  服务器  json  parameters  dojo  

 
在dojo中使用JSON-RPC
1        JSON-RPC规范
JSON-RPC 是一种轻量级远程过程调用协议,类似JAVA的RMI和.NET中的Remoting。在此协议中,通讯双方的请求对象和响应对象使用JSON编码方式,通过前面的“JSON编码简介”可以简单了解其编码规则。
 
1.1      请求,响应和通知对象
首先客户端向远程服务器发出远程调用请求,该请求对象具有三个属性的:
 
method – 远程调用的方法名称的字符串。
params – 方法参数数组。
id –   请求 ID 。可以为任何类型,用于将响应与其应答的请求相匹配。
 
服务端的响应对象也具有三个属性:
 
result - 被调用方法返回的结果对象。在调用该方法时发生错误时必须为 null
error - error 对象。如果在调用方法时设置,没有发生错误时必须为 null
id - 它必须是与响应的请求相同的 ID
 
通知是不需要响应的特殊请求类型。通知对象与请求对象基本相同,唯一的一点区别通知的id必须为 null。
 
1.2      通讯协议
JSON-RPC没有规定具体的传输协议,可以在普通的TCP/IP socket通讯中使用,也可以使用HTTP协议。
 
在socket通讯中,序列化的请求和响应对象通过字节流的方式在通讯双方传递。请求和响应可以在任意时间发起,除了通知外,通讯双方必须对所有请求对象进行响应。一个响应只能发送到一个请求方。在关闭连接时,所有通讯双方未响应的请求必须抛出异常。无效的请求或响应必须引起连接关闭。
 
在带有某些限制的情况下,可以使用HTTP协议进行通讯。通讯双方的会话,包括一个HTTP客户端和一个HTTP服务器,可能跨越多个HTTP请求。客户端可以使用一个HTTP POST请求向对方发送一个或多个序列化的请求、通知或响应对象。服务器端必须对请求对象进行响应,也可以发送自己的通知或请求对象。客户端必须使用另一个HTTP POST对接收到的请求对象进行响应。为了实现服务器端向客户端的主动通讯,客户端可以发送空的HTTP POST以重新建立连接。无效请求将导致连接关闭。在客户端,一个无效的响应将对所有没有应答的请求抛出异常,关闭连接也将对所有没有应答的请求抛出异常。
 
1.3      会话举例
简单的会话举例如下:
调用服务器echo方法,参数Hello JSON-RPC ,id为1
--> { "method": "echo", "params": ["Hello JSON-RPC"], "id": 1}
服务器响应结果为Hello JSON-RPC,对应id为1,没有错误
<-- { "result": "Hello JSON-RPC", "error": null, "id": 1}
 
带有通知的通讯会话
...
--> {"method": "postMessage", "params": ["Hello all!"], "id": 99}
<-- {"result": 1, "error": null, "id": 99}
<-- {"method": "handleMessage", "params": ["user1", "we were just talking"], "id": null}
<-- {"method": "handleMessage", "params": ["user3", "sorry, gotta go now, ttyl"], "id": null}
--> {"method": "postMessage", "params": ["I have a question:"], "id": 101}
<-- {"method": "userLeft", "params": ["user3"], "id": null}
<-- {"result": 1, "error": null, "id": 101}
...
 
2        Dojo对JSON-RPC的支持
2.1      dojo.io.blind 介绍
dojo.io包中提供了对XMLHTTP和一些其他更复杂的传输结构的支持。在dojo.io 包中一般最常使用的是dojo.io.bind()方法。dojo.io.blind()是一个标准的异步的请求API,它包含了各种传输层(transport layers),包括Iframe请求、XMLHTTP、mod_pubsub、LivePage等等。Dojo会根据当前的请求选择最合适的传输方法。下面的代码创建了一个请求(request),这个请求会从指定的URL返回字符串,并且指定了一个处理函数。
dojo.io.bind({
    url:  " http://foo.bar.com/sampleData.txt " ,
    load:  function (type, data, evt){  /* do something with the data  */  },
    mimetype:  " text/plain "
});
 
如果在请求过程中出错了怎么办呢?我们可以再指定一个错误处理函数:
dojo.io.bind({
    url: "http://foo.bar.com/sampleData.txt",
    load: function(type, data, evt){ /*do something with the data */ },
    error: function(type, error){ /*do something with the error*/ },
    mimetype: "text/plain"
});

同样也可以只创建一个单独的函数,然后在内部根据返回类型来进行不同的处理:
dojo.io.bind({
    url: "http://foo.bar.com/sampleData.txt",
    handle: function(type, data, evt){
        if(type == "load"){
            // do something with the data object
        }else if(type == "error"){
            // here, "data" is our error object
            // respond to the error here
        }else{
            // other types of events might get passed, handle them here
        }
    },
    mimetype: "text/plain"
});
 
下面的代码提交一段javascript程序段,然后让服务器运行它,一般我们这么做是为了加速程序运行,注意mimetype:
dojo.io.bind({
    url: "http://foo.bar.com/sampleData.js",
    load: function(type, evaldObj){ /* do something */ },
    mimetype: "text/javascript"
});
 
如果想确保程序使用XMLHTTP,可以指定传输协议类型:
dojo.io.bind({
    url: "http://foo.bar.com/sampleData.js",
    load: function(type, evaldObj){ /* do something */ },
    mimetype: "text/plain", // get plain text, don't eval()
    transport: "XMLHTTPTransport"
});
 
dojo.io.bind()同样支持来自于表单提交的数据。
dojo.io.bind({
    url: "http://foo.bar.com/processForm.cgi",
    load: function(type, evaldObj){ /* do something */ },
    formNode: document.getElementById("formToSubmit")
});
 
2.2      RPC
Dojo通过dojo.io.bind提供了简单,强大的方法使用多种多样的I/O functions。但是在开发过程中,程序员会调用很多很多I/O,这同时会给服务器和客户端加重负担。Dojo的RPC功能就是为了减少开发负担,使代码更加精简易用而产生的。Dojo不仅提供了基本的RPC client包,而且还扩展了它,使它支持JSON-RPC服务和YAHOO服务。假定有一个需要调用服务器端程序的小程序,假设要调用add(x,y)和subtract(x,y)。在没有特殊情况的条件下,客户端会这样写:
add = function(x,y) {
    request = {x: x, y: y};
    dojo.io.bind({
            url: "add.php",
            load: onAddResults,
            mimetype: "text/plain",
        content: request
    });
}

subtract = function(x,y) {
    request = {x: x, y: y};
        dojo.io.bind({
            url: "subract",
            load: onSubtractResults,
            mimetype: "text/plain"
        content: request
    });
}
 
这不是很难。但是这只是一个非常简单的程序。如果要调用在服务器上30个不同method会怎么样呢?开发人员可能要重复的写几乎一样的代码一遍又一遍,每次都要创建一个请求类(request object),设定URL,设定变量等等。这不仅容易出错,而且还很枯燥。Dojo的RPC客户端简化了这个过程,首先创建一个服务定义文件testService.smd,SMD(Simple Method Description )是远程调用接口的描述文件,可以视为简化的WSDL文件
{
    "serviceType": "JSON-RPC", 
    "serviceURL": "rpcProcessor.php", 
    "methods":[ 
        {
            "name": "add", 
            "parameters":[
                {"name": "x"},
                {"name": "y"}    
            ]
        },
        {
            "name": "subtract", 
            "parameters":[
                {"name": "x"},
                {"name": "y"}    
            ]
        }

    ]
}
 
以上就是对于远程对象接口定义。一旦定义创建完毕,要使用服务器的方法则可以首先根据服务接口定义创建远程对象,然后调用相应的方法:
var myObject = new dojo.rpc.JsonService(testService.smd);
myObject.add(3,5);
 
服务器端的myObject.add()会返回一个延缓类(deferred object)。延缓类允许开发者根据返回数据的类型附加一个或更多的回叫(callbacks)和错误处理(errbacks)。这里有一个简单的例子:
var myDeferred = myObject.add(3,5);
myDeferred.addCallback(contentCallBack);
 
或者象最后的例子中,在一条语句中完成:
testClass.contentC().addCallbacks(contentCallBack,contentErrBack);
 
在dojo中,可以随意添加回叫方法(callback),它们会按照定义的顺序被调用。
以上的例子都是基于dojo.rpc.JsonService的。Dojo中还可以使用dojo.rpc.YahooService,规范和结构都是一样的。这两个类都是继承了dojo.rpc.RpcService。
 
3        具体的例子
该例子从dojo提供的rpc例子中改进而来,修改了一些错误,增加了返回JSON对象的函数。其服务端包括两个PHP脚本,以及JSON的PHP实现。客户端包括两个SMD定义和一个HTML页面。当然还应当有dojo的脚本以及JSON的javascript实现。
3.1      服务端
实际的服务类,其中myecho为简单的回显函数,contentB返回固定的contentB字符串,contentC返回一段JSON编码的对象,在客户端可以使用JSON进行解码以得到一个响应对象。这就演示了如何通过JSON-RPC传送复杂的对象。add实现了简单的加法。
<?php
class testClass {
 
       function myecho ($somestring) {
              return $somestring;
       }
 
       function contentB () {
              return "Content B";
       }
 
       function contentC () {
              return '{"user":"firefight", "password":"sorry, I can not tell you"}';
       }
 
       function add($x,$y) {
              return $x + $y;
       }
}
?>
服务中介
负责实例化服务类,接收客户端请求,使用JSON反序列化,然后根据请求内容调用正确的服务类方法。在程序中,如果客户端调用的是triggerRpcError,则返回一个带有错误的响应对象,模拟了服务器处理失败的情况。
<?php
       require_once("./JSON.php");
      
       // ensure that we don't try to send "html" down to the client
       header("Content-Type: text/plain");
       $json = new Services_JSON;
      
       //Get request object
       $req = $json->decode($HTTP_RAW_POST_DATA);
 
       include("./testClass.php");
      
       $testObject = new testClass();
      
       //Prepare results
       $results = array();
       $results['error'] = null;
      
       $method = $req->method;
       if ($method != "triggerRpcError") {
              $ret = call_user_func_array(array($testObject,$method),$req->params);
              $results['result'] = $ret;
       } else {
              $results['error'] = "Triggered RPC Error test";
       }
      
       //Set result id
       $results['id'] = $req->id;
 
       $encoded = $json->encode($results);
 
       print $encoded;
?>
3.2      客户端
 
带有一个testClass接口描述的SMD定义。
{
       "SMDVersion":".1",
       "objectName":"testClass",
       "serviceType":"JSON-RPC",
       "serviceURL":"test_JsonRPC.php",
       "methods":[
              {
                     "name":"myecho",
                     "parameters":[
                            {
                                   "name":"somestring",
                                   "type":"STRING"
                            }
                     ]
              },
              {
                     "name":"contentB"
              },
              {
                     "name":"contentC"
              },
              {
                     "name":"add",
                     "parameters":[
                            {
                                   "name":"x",
                                   "type":"STRING"
                            },
                            {
                                   "name":"y",
                                   "type":"STRING"
                            }
                     ]
              },
              {
                     "name":"triggerRpcError"
              },
 
       ]
}
 
为了测试连接错误的情况,创建一个带有无效URL地址的SMD文件,其中包括对erroringClass的接口描述,当然对erroringClass的远程调用会在连接时就失败。
{
       "SMDVersion":".1",
       "objectName":"erroringClass",
       "serviceType":"JSON-RPC",
       "serviceURL":"badUrlForTesting",
       "methods":[
              {
                     "name":"content"
              },
       ]
}
 
客户页面,提供按钮和结果显示。
<script type="text/javascript">
        var djConfig = {isDebug: true,debugContainerId: "dojoDebug" };
        //djConfig.debugAtAllCosts = true;
</script>
 
<script type="text/javascript" src="../dojo/dojo.js"></script>
<script type="text/javascript" src="../common/json.js"></script>
 
<script type="text/javascript">
       dojo.require("dojo.debug.Firebug");
       dojo.require("dojo.widget.*");
       dojo.require("dojo.widget.Button");
       dojo.require("dojo.rpc.JsonService");
       dojo.require("dojo.rpc.Deferred");
 
       function contentCallBack(result) {
              var handlerNode = document.getElementById("ReturnedContent");
              handlerNode.innerHTML = "<p>" + result + "</p>" ;
       }
 
       function contentErrBack(obj) {
              dojo.debug(obj);
              var handlerNode = document.getElementById("ReturnedContent");
              handlerNode.innerHTML = "<p> Error! Please refer to debug below! </p>" ;
       }
 
       function callBackForContentC(result)
       {
              var r = JSON.parse(result);
              var str = "User: " + r.user + "; Password: " + r.password;
              var handlerNode = document.getElementById("ReturnedContent");
              handlerNode.innerHTML = "<p>" + str + "</p>" ;
       }
      
       var testClass = new dojo.rpc.JsonService("testClass.smd");
       var erroringClass=new dojo.rpc.JsonService("erroringClass.smd");
</script>
 
</head>
 
<body>
<div id="RPC">
       <h2>Push these buttons to execute code in the button.</h2>
       <h3>Results will be returned and show in "Returned Content"</h3>
</div>
 
<br>
 
<div id="ReturnedContent">
<p>None.</p>
</div>
 
<br>
 
<div class="box">
       <button dojoType="Button" οnclick='testClass.myecho("blah").addCallbacks(contentCallBack,contentErrBack);'>
              Echo blah
       </button>
       <button dojoType="Button" οnclick='testClass.contentB().addCallbacks(contentCallBack,contentErrBack);'>
              ContentB()
       </button>
       <button dojoType="Button" οnclick='testClass.contentC().addCallbacks(callBackForContentC,contentErrBack);'>
              Get Object
       </button>
       <button dojoType="Button" οnclick='testClass.add(5,6).addCallbacks(contentCallBack,contentErrBack);'>
              5+6=?
       </button>
       <button dojoType="Button" οnclick='testClass.triggerRpcError().addCallbacks(contentCallBack,contentErrBack);'>
              Error from server
       </button>
       <button dojoType="Button" οnclick='erroringClass.content().addCallbacks(contentCallBack,contentErrBack);'>
              Error from client
       </button>
</div>
 
<br clear=both>
 
<div id="dojoDebug">
<h2>Debug Log:</h2>      
</div>
 
</head>
</html>
 
参考资料:
JSON-RPC Specifications
http://json-rpc.org/wiki/specification
Dojo book 以及burnet的中文翻译
http://www.blogjava.net/burnet/
 
 
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/firefight/article/details/1443798

智能推荐

使用nginx解决浏览器跨域问题_nginx不停的xhr-程序员宅基地

文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr

在 Oracle 中配置 extproc 以访问 ST_Geometry-程序员宅基地

文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc

Linux C++ gbk转为utf-8_linux c++ gbk->utf8-程序员宅基地

文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8

IMP-00009: 导出文件异常结束-程序员宅基地

文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束

python程序员需要深入掌握的技能_Python用数据说明程序员需要掌握的技能-程序员宅基地

文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求

Spring @Service生成bean名称的规则(当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致)_@service beanname-程序员宅基地

文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname

随便推点

二叉树的各种创建方法_二叉树的建立-程序员宅基地

文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include&lt;stdio.h&gt;#include&lt;string.h&gt;#include&lt;stdlib.h&gt;#include&lt;malloc.h&gt;#include&lt;iostream&gt;#include&lt;stack&gt;#include&lt;queue&gt;using namespace std;typed_二叉树的建立

解决asp.net导出excel时中文文件名乱码_asp.net utf8 导出中文字符乱码-程序员宅基地

文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码

笔记-编译原理-实验一-词法分析器设计_对pl/0作以下修改扩充。增加单词-程序员宅基地

文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词

android adb shell 权限,android adb shell权限被拒绝-程序员宅基地

文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限

投影仪-相机标定_相机-投影仪标定-程序员宅基地

文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定

Wayland架构、渲染、硬件支持-程序员宅基地

文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland

推荐文章

热门文章

相关标签