Ruby's Louvre

每天学习一点点算法

导航

rails与jsonp

涉及后台的东西,我也不敢保证对错,总之写出来如果有错望各位高人不吝赐教吧。跨域的需要大多数出于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再说吧。

posted on 2010-01-04 22:11  司徒正美  阅读(3198)  评论(0编辑  收藏  举报