涉及后台的东西,我也不敢保证对错,总之写出来如果有错望各位高人不吝赐教吧。跨域的需要大多数出于web2.0的mashup,如果我的网站想搞一个地图,但自己实现没有钱没有技术显然难度太大,这样可以利用google map,又如想加入天气预报,这样也可以调用yahoo网站提供的相关服务。这些服务常常是一些HTTP GET形式的API。那么我们就发起一个get请求就是,往往可以得到一段文本或xml,然后我们就可以搞鼓了……本文提到的jsonp,或许说动态JS标签更合适些,与上面提到的情形有些不同。上面的google,yahoo都不属性于你的东西,你只要传入URI外加几个参数,它就会返回一堆东西,然后你再用它们提供的代码加工一下。如本博客引用feedsky的服务,就是这种情况。由于这情况太简单,不在本文讨论范围内。我所说的情况是有两个网站,它们都是我们开发的。我们从一个网站发送请求,另一个网站接收请来,返回数据,交由原网站处理。发送的请求当然是get请求,就是普通动态加载JS文件,不过这JS文件是另一个网站生成的。看一下面代码:
<!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Demo by 司徒正美</title> <script type="text/javascript"> //在原页设置函数体 function say(words) { alert(words); } </script> </head> <body> <!--在远程js文件上调用本地函数//--> <script src="http://yabooo.org/remote.js" type="text/javascript"></script> </body> </html>
JS文件很简单:
say("司徒正美!")
弹出司徒正美
一般认为这就是JSONP的原型,用一个以js文件作结尾的URL请求另一个域的服务器,然后让它把所需要的东西全部搞成一个JSON对象,作为本地函数的参数传入执行。下面的例子需要rails支持,有兴趣的可以看看。
设置控制器:
class FooController < ApplicationController def bar respond_to do |format| format.html format.js do render :update do |page| page << %{ say("司徒正美!") } end end end end end
设置layout:
#D:\demo\views\layouts <!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Demo</title> </head> <body> <h1>layout</h1> <%= yield %> </body> </html>
设置视图:
#D:\polyline\app\views\foo\bar.html.erb <script type="text/javascript"> //在原页设置函数体 function say(words) { alert(words); } </script> <!--在远程js文件上调用本地函数//--> <%= javascript_include_tag "/foo/bar.js" %>
设置路由规则:
map.resources :foo, :collection => { :bar => :get}
然后打开http://localhost:3000/foo/bar.html,就会看到alert了,证明调用成功。
下面是一个增强版,由于可怜的我没有网站与域名,姑且用另一个action冒充第二个网站吧,反正我们都知道JS文件是不受域名限制的,很完美地绕过所谓的同源策略。
class FooController < ApplicationController def bar end def laaa respond_to do |format| format.html format.js do render :update do |page| page << %{ say(" laaa") } end end end end end
map.resources :foo, :collection => { :bar => :get,:laaa => :get}
<script type="text/javascript"> //在原页设置函数体 function say(words) { alert(words); } var url = "http://localhost:3000/foo/laaa.js"; // URL of the external script // this shows dynamic script insertion var script = document.createElement('script'); script.setAttribute('src', url+"?"+(+new Date)); script.setAttribute("charset", "utf-8"); // load the script document.getElementsByTagName('head')[0].appendChild(script); </script>
再修改一下,这次传入json做参数。rails在这方面做得很好,可以直接把从数据库取得的记录用to_json转换成JSON,不过我现在不需要这么复杂。
class FooController < ApplicationController def bar end def laaa respond_to do |format| format.html format.js do if params[:jsonp].present? callback = params[:jsonp] render :update do |page| page << %{ #{callback}({"a":"司徒正美","b":"RestlessDream"}) } end end end end end end
<script type="text/javascript"> //在原页设置函数体 function say(json) { alert(json.a+" : "+json.b); } function jsonp(url,callback){ if (url.indexOf("?") > -1) url += "&jsonp="+callback else url += "?jsonp="+callback url += "&nocache=" +(+new Date()); // prevent caching var script = document.createElement("script"); script.setAttribute("src",url); script.setAttribute("type","text/javascript"); script.setAttribute("charset", "utf-8"); document.getElementsByTagName('head')[0].appendChild(script); } jsonp("http://localhost:3000/foo/laaa.js","say") </script>
这次会弹出司徒正美 : RestlessDream,当然现实的需要会很复杂,会对传回来的JSON进行深加工,很少会像这样对号入座。不过处理JSON总比处理XML好。有时可能加载N多JSON,那么后台就要设置更多的分支,前台动态生成的script标签也随之增加,那么我们加载完这些标签后,可以立即删除,这是没有问题的。
好了,这样就完了,更复杂更完善的东西结合Ajax再说吧。