在 PHPRPC 3.0 for javascript 客户端中,关于跨域调用的使用,跟非跨域调用没有区别。只要在客户端中使用跨域服务器的绝对地址就可以了。例如上一节中的例子,如果其中客户端初始化语句改为:
view plaincopy to clipboardprint?
1. var rpc = new PHPRPC_Client('http://www.phprpc.org/samples/sha1.php', ['sha1']);
,并且客户端网页与该网站不在同一个域中的话,那么它就会自动转为跨域调用模式。而服务器端不需要做任何特殊设置。
但跨域调用在 PHPRPC 中是如何实现的呢?
ajax 跨域调用
PHPRPC 与其它大部分 ajax 框架不同,大部分 ajax 框架在数据交互方面都是面向数据的,而 PHPRPC 是面向服务的。面向数据的情况就是服务器端返回的内容没有固定的格式,服务器端和客户端要使用何种格式进行数据交换,完全由开发者自己说了算,这从表面上看似乎给了开发者足够的灵活性,但结果却是增加了开发者的负担(需要自己设计格式并自己实现数据解析),并且无法做到在浏览器中直接跨域调用。
PHPRPC 明确的定义了数据交换的格式。你可以认为它与 XML-RPC、SOAP、JSON-RPC 对数据交换格式的定义方式相似,但实际上,PHPRPC 与它们也有很大的区别。如果你自己考察过 XML-RPC、SOAP、JSON-RPC 的数据交换格式定义的话,你会发现它们都陷入了一个自恋的陷阱。例如,XML-RPC 和 SOAP 完全被禁锢在了 XML 当中,而 JSON-RPC 又把这个极端推到了 JSON 这种格式上。这样做的结果就是:你总是想让别人来适应你,但却忘记了现实中可能会存在别人根本无法适应你的情况。
在浏览器中实现跨域调用总的来说有两种方式:
一种是通过本地服务器做代理,替浏览器转发请求给跨域服务器,同时替跨域服务器将响应内容转发给浏览器。这种方式实际上是一种伪跨域方式,你必须要有本地服务器的支持才可以做到。
而另一种方式就是直接在页面里插入跨域脚本。这种方式并不新鲜,早在很久以前广告商和做流量统计的网站就已经在用这种技术了。这种方式的好处就是不需要本地服务器支持即可调用跨域服务器的服务,可以实现真正的跨域调用。当然,缺点就是请求只能以 HTTP GET 方法提交。
在 HTTP 协议中虽然并没有规定用于 HTTP 请求的 URL 的长度上限,但是实际上不论是浏览器还是 Web 服务器,在实现时都对它做了限制,一般是在 4K 字节左右(这个数字跟具体的浏览器和 Web 服务器有关,不是统一的)。因此,通过 HTTP GET 方法提交请求的长度是不能太长的。
另外,HTTP 协议规定,HTTP GET 方法应该是幂等的,意思就是 HTTP GET 请求不应该改变服务器端的数据,比如从 Web 服务器上删除一个文件,或者向数据库服务器中的添加一条记录,诸如此类的操作都不是幂等的。但实际上,几乎所有现代的 Web 应用都破坏了这条幂等原则。
如果我们要实现真正的跨域,我们就只有后一条路可以走。但是对于这条路来说,XML-RPC、SOAP、JSON-RPC 都走不通了。因为它们返回的响应不兼容 JavaScript。这里面最大的一个笑话就是 JSON-RPC,JSON-RPC 所使用的数据交换格式 JSON 实际上是 JavaScript 的一个子集,但是在 JSON-RPC 协议中,它返回的响应却只是一个用 JSON 来做的数据定义,接下来它就再也不能做什么啦。所以,JSON-RPC 成为了一个使用 JavaScript(JSON)却不支持插入跨域脚本实现浏览器中跨域调用的,完全因为自恋而丧失了实用性的 RPC 协议。
而 PHPRPC 在设计之初就考虑到了适应性这一点。它听取并履行了伟大的穆斯林智者的话:“如果山不能来我们这边,我们就到山那边去吧!”。因此它做到了真正的跨域调用。因为它没有陷入对 PHP 序列化格式的自恋之中,而是对已有的协议(HTTP)和基础(浏览器)做了良好的适应。我们来看它是如何做到的:
它的请求使用了 HTTP 应用中广泛支持的 MIME 中的 application/x-www-form-urlencoded 格式进行封装。因此,它简化了在实现时对 GET 和 POST 方法请求中数据差别的处理。
为了减小对原始请求直接进行 URL 编码而产生的负载和防止编码前与编码后长度比的不固定,PHPRPC 协议对原始请求首先做了 Base64 编码,然后再进行 URL 编码,这样做的结果使得请求中原始数据的长度和实际数据的长度固定在了一个大约 3 : 4 的比例上,这就很好的避免了编码后数据远远大于编码前数据这种情况的发生,同时,我们又可以做到对编码后数据长度的预知。
它的响应使用了 JavaScript 的赋值语句格式,并且允许在响应的最后添加请求中携带的一个 callback 信息,这种响应格式使得采用插入跨域脚本进行跨域调用的可能变成了现实。
而对于响应中原始数据的编码方式也采用了两种可选的方案:一种是同请求一样采用 Base64 编码,这种编码方式很好的保证了编码后的数据中不会出现数据两端的定界符(双引号),因此方便了数据的解析。而另一种方案是采用 JavaScript 所能识别的转义字符串格式,这种格式与 C 语言中的转义字符串格式极为相似(仅有几个特殊符号的转义方式有所不同),这种格式的好处是,对于浏览器客户端来说完全不需要通过 JavaScript 程序去解析,直接由浏览器的 JavaScript 引擎就可以解析,因此,极大的提高了浏览器客户端的处理效率。服务器对这两种响应格式的选择是通过客户端请求中设置的参数来决定的。
正是有了这种良好的适应性设计,才使得 PHPRPC 从一开始就支持真正的浏览器跨域调用。这是其它同类协议所不能相比的。