link_to是Rails中提供的一个内置方法,用于产生一个超链接,常用的方法通常是link_to "链接文字", options = {}, html_options = {}, 如果我们不指定提交的方法,默认情况下将产生一个HTTP GET请求,但是我们也可以通过指定提交方法来避免某些敏感数据的泄漏。
在《Agile web development with Rails》一书中,有这样一个练习题:为购物网站上面的图片添加一个超链接,当点击该图片时请求名为store的controller下add_to_cart action,同时要求使用POST方式来请求。代码如下所示:
 <%= link_to image_tag(product.image_url),  options = {:action => "add_to_cart"},  html_options={:method => "post"} %>
<%= link_to image_tag(product.image_url),  options = {:action => "add_to_cart"},  html_options={:method => "post"} %> 
其中第一个参数image_tag是一个内置方法,用于产生一个<img src="xxx" />的HTML标签,而options接受一个hash容器,在这个容器中只有一个参数action,它告诉了请求对应的action,于是rails会在构建<a>表情的href属性时将其转换为:http:localhost:3000/store/add_to_cart 这种形式:即请求StoreController下的add_to_cart方法。最重要的一定是html_options参数,它也接受一个hash容器,通过method="post"来告诉浏览器,采用post方式来提交请求。
但是我们知道post通常是在form中才用到的,这里并没有form的定义,那么是如何采用post方式来进行提交的呢?通过阅读浏览器解析后的源代码,我们可以发现rails在编译期间做了很巧妙的处理:它创建了一个动态的、隐藏的表单来提交:
 <a href="/store/add_to_cart" onclick="..."><img alt="Auto" src="/images/auto.jpg?1265130093" /></a>
<a href="/store/add_to_cart" onclick="..."><img alt="Auto" src="/images/auto.jpg?1265130093" /></a> 

下面就是onClick的内容:


 {
{
 //创建一个隐式的表单对象,并设置提交方式为POST,已经设置提交的URL
  //创建一个隐式的表单对象,并设置提交方式为POST,已经设置提交的URL
 var f = document.createElement('form');
  var f = document.createElement('form');  
 f.style.display = 'none';
  f.style.display = 'none'; 
 this.parentNode.appendChild(f);
  this.parentNode.appendChild(f); 
 f.method = 'POST';
  f.method = 'POST'; 
 f.action = this.href;
  f.action = this.href;

 //为表单添加一个隐藏域,指定提交的方式参数
  //为表单添加一个隐藏域,指定提交的方式参数
 var m = document.createElement('input');
  var m = document.createElement('input'); 
 m.setAttribute('type', 'hidden');
  m.setAttribute('type', 'hidden'); 
 m.setAttribute('name', '_method');
  m.setAttribute('name', '_method'); 
 m.setAttribute('value', 'post');
  m.setAttribute('value', 'post');
 f.appendChild(m);
  f.appendChild(m);
 
  
 //为表单添加一个隐藏域,指定当前的Session token key,防止重复提交
  //为表单添加一个隐藏域,指定当前的Session token key,防止重复提交
 var s = document.createElement('input');
  var s = document.createElement('input'); 
 s.setAttribute('type', 'hidden');
  s.setAttribute('type', 'hidden'); 
 s.setAttribute('name', 'authenticity_token');
  s.setAttribute('name', 'authenticity_token'); 
 s.setAttribute('value', '6c02dccc61c8e299bf1765bd0414355e9d8a4815');
  s.setAttribute('value', '6c02dccc61c8e299bf1765bd0414355e9d8a4815'); 
 f.appendChild(s);
  f.appendChild(s);
 
  
 //动态提交表单
  //动态提交表单
 f.submit();
  f.submit();
 
  
 return false;"
  return false;"
 }
}
这就是采用post方式提交请求的“秘密”,rails的实现相当优雅!可是如果我们把上面的link_to代码稍微改一下,如下面所示,会有什么结果呢?
 <%= link_to image_tag(product.image_url),  {:action => "add_to_cart", :method => "post"} %>
<%= link_to image_tag(product.image_url),  {:action => "add_to_cart", :method => "post"} %> 
我们来看看最终产生的页面源代码
 <a href="/store/add_to_cart?method=post"><img alt="Auto" src="/images/auto.jpg?1265130093" /></a>
<a href="/store/add_to_cart?method=post"><img alt="Auto" src="/images/auto.jpg?1265130093" /></a>
很明显,method=post变成了URL的请求参数,而不是HTTP 请求报头了。也就是说这里产生的是一个HTTP GET请求:http://localhost:3000/store/add_to_cart?method=post,而正确的请求应该是:http://localhost:3000/store/add_to_cart
所以我们一定要记住:options={}是用来传递请求参数的,而html_options={}是用来设置请求报头的,不能搞混!
 -------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要尽力打好一手烂牌。 
	posted on 2010-05-17 16:41 
Paul Lin 阅读(6525) 
评论(4)  编辑  收藏  所属分类: 
RoR