在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

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签