在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

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法