这两天用Ajax连接后端JSON-RPC的服务, 后端的service是用C++写的,用到的是cppcms,作者给的例子是python写的,需要设置Content- Type:application/json,经测试,python可以正常访问到这个service。

后端由于已经限定了Content-Type, 考虑到需要和其他服务器端通信也需要到这个设置,不好做出修改。所以在浏览器端也要进行该设置,在查阅了资料后,发现请求头的类型可以自己任意设置,例如 setRequestHeader(name,value);

新建了一个本地的html测试后,设置了xhr.setRequestHeader(“Content-Type”,”application/json”);

测试的结果是ie8 能够正常设置Content-Type,能够和后端交互,服务器端返回status200, 获得正确结果。

但是chrome里和ff里反复设置content-type,都不成功,在浏览器的网络面板里也没看到发出去的请求。用fiddler等工具也没有发现正确的请求头部和正确的应答结果。

在chrome里只有设置了xhr.setRequestHeader(“Content-Type”,”application/x-www- form-urlencoded,application/json; charset=utf-8″);才能在网络面板里看到,但是被浏览器给canceled掉了.

而且Content-Type中在第二个设置了application/json也不能通过,从这个可以发现,application/json; 必须要设置为第一个参数。

调了很长都未果,后来用firebug的rest插件模拟一个请求,设置content-type:application/json, 参数“{‘method’:’sum’,’params’:[1,2],’id’:1}”,得到正确的结果3,考虑到这些发现了是一个跨域的情况,解决了 同源策略问题后,得到了正确的结果,Content-Type也正确的设置上了。

开始时由于没有正确的设置上Content-Type,思路限定在Content-Type值的设置方法上了,例如HTTP请求一些设置依赖关系, 例如是否要同时设置上Accept,Content-length等。 最后才发现是同源策略影响到了setRequestHeader正确的设置。

简单说一下JSON-RPC

JSON-RPC 分为1.0 和2.0

1.0的参数设置 可以参考http://json-rpc.org/wiki/specification

  • · method – AString containing the name of the method to be invoked.
  • · params – AnArray of objects to pass as arguments to the method.
  • · id – The request id. This can be of anytype. It is used to match the response with the request that it is replying to.

method:是请求的方法名

params:是一个参数数组

id:是一个唯一的id

一个例子是“{‘method’:’sum’,’params’:[1,2],’id’:1}”;

2.0多加了一个版本号 参考 http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal

{“jsonrpc”: “2.0″, “method”:”subtract”, “params”: [42, 23], “id”: 1}

在项目中用到的是1.0的格式

测试代码如下

Source Code
1
2
3
4
5
6
7
8
9
10
11
12
var xhr = new XMLHttpRequest();
var params = "{'method':'sum','params':[1,2],'id':1}" ;
xhr.open( "post" , “path /rpc”, true );
xhr.setRequestHeader( "Content-Type" , "application/json" ); //要再open方法后调用
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 ) {
if (xhr.status >= 200 &&xhr.status < 300 || xhr.status === 304 ) {
alert( "ok" )
}
}
}
xhr.send(params)

如果用jquery调用更简单


Source Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$.ajax({
url: “/rpc",
type: "post" ,
dataType: "json" ,
data: '{"method":"sum","params":[1,2],"id":1}' ,
contentType: "application/json; charset=utf-8" ,
beforeSend: function(x) {
//这个不需要设置,要不可能在有得浏览器中出现重复的content-type
//x.setRequestHeader("Content-Type", "application/json;charset=utf-8");
},
success: function(json) {
alert( "ok" );
},
error: function(x, e) {
alert( "error" );
},
complete: function(x) {
alert( "complete" );
}
})

Web开发由于需要经常进行ajax请求,很多时候涉及到跨域,由于浏览器的同源策略,所以会影响到正确的请求和返回数据。
关于同源策略(Same Origin Policy)
最早源于NetscapeNavigator 2.0,在浏览器端是一个重要的安全策略。同源是指,域名相同,应用层协议相同,端口相同,当这些属性完全一致时时才被认为是同源。所以当遇到下面的访问时都要考虑到跨域的问题。

说明:

http://www.test.com/

父域名是test.com.
子域是
www.test.com
lab.test.com等
举一个例子
假如当前页面是

http://www.test.com/1/index.html

比较的url 结果 原因
http://www.test.com/1/index.html

http://www.test.com/1/index.html

成功 同协议同host
http://www.test.com/1/index.html

http://www.test.com/2/index.html

成功 同协议同host
不同文件夹
http://www.test.com/1/index.html

http://www.test.com:82/1/other.html

失败 不同端口
80和82
http://www.test.com/1/index.html

https://www.test.com/1/other.html

失败 不同协议
http — https
http://www.test.com/1/index.html

http://en.test.com/1/other.html

失败 不同host(子域不同)
www —en
http://www.test.com/1/index.html

http://test.com/1/other.html

失败 不同host
www—test
http://www.test.com/1/index.html

http://other.www.test.com/dir/other.html

失败 不同host
www—other.www

跨域的一些方案
1 跨子域之间的相互访问
例如父域名都是test.com,子域是www.test.com 和other.test.com,
例如在www.test.com要通过ajax请求other.test.com页面里的一个txt页面other.test.com/1.txt.这样由于是跨域是不能得到的.

不过这个问题是跨子域,可以通过把两个页面都要设置domain = test.com”。

在主页面
test.com的html里
首先要包括子页面的一个html文件
然后设置domain
document.domain = “ test.com”;
获得这个iframe的窗口,这里用jquery框架方便调用
contentWindow是获得框架中的窗口,类似于window

1 var crossIframe = document.getElementById(‘ crossIframe ‘).contentWindow.$;
2   crossIframe.get(“http: //other.test.com/1.txt”,function(data){
3       alert(“成功“);
4   });
5   }

在other.test.com/iframe.html里要include jquery库文件
同样也要也要设置
document.domain = “test.com”;
这样就可以在test.com的html页面中成功获取1.txt了

2 用注入方式跨域,经常说的jsonp
注入的主要跨域思路是建一个script脚本,append进head或者是body.
有两种方法
第一个种是在
该脚本会向www.xxxxx.com/getResult 发送请求,该服务返回的格式要是javascript可以执行的代码,当script调用成功后,会自动调用callback.
这里需要的就是www.abc.com 里要有一个callback函数,以便刚才的script脚本成功执行后的回调。
function callback(data){
alert(data); //data为script脚本成功调用返回的数据
}

动态注入的方法:
把刚才的url传入即可,动态生成一个script元素,然后插入document或者head

1 function JSON(url){ // 调用JSONP服务器,url为请求服务器地址
2       var script =document.createElement(“script”);
3       script.setAttribute(“type”,”text/javascript”);
4       script.setAttribute(“src”,url);
5       script.setAttribute(“id”,url);
6       document.appendChild(script);
7   }

如果利用jquery会更方便些
例如

1 Var url = “<a href= "http://www.xyz.com/getResult " >www.xyz.com/getResult</a>?callback=?”;
2   $.get(url, {“name”:”hiro”}, function (data){ },”json”);

1.2版本以上只要上面写一个占位符?就可以,Jquery会把自动会把callback=?转换为注入方式

或者用jquery的$.ajax

 

1 $.ajax({
2      type:“get”,
3      data:{},
4      url:url,
5      dataType:“jsonp”,
6      jsonp:“callback”,
7      success: function (data){}
8 });<strong></strong>

 

3利用iframe进行两个站点的跨域
刚才所说的是跨子域的iframe方法,如果跨两个域的方法会更麻烦一些,利用iframe + location hash值进行通信。
4用后端语言作为中间层
在本域下,由于服务器端语言没有跨域的限制,所以可以用例如java c#,python等去请求服务,然后再返回本域的数据。