在带有超链接的rails中发出POST请求

我需要在页面上有一堆链接,每个链接都会向不同的控制器发出POST。 但是当我使用普通链接时,我得到一个ActionController :: InvalidAuthenticityToken错误。 我理解这是因为缺少authenticity_token值。 但我不想使用表单来进行POST,因为我希望它们是链接而不是按钮。 事实是,我想完全控制链接和按钮的样式,只是不要为我做。 做这些事情的标准方法是什么?

你有很多选择。

  1. 禁用AT检查: protect_from_forgery :only => []
  2. 将onclick处理程序分配给链接并使用javascript提交隐藏的表单。
  3. 生成视图时抓取AT并将其添加为请求参数。

顺便说一下,你究竟如何只使用’href’属性来’发布’请求? 我认为forms是必须的。

从技术上讲,你应该使用按钮和表格来处理非GET事情; 超链接故意不允许GET以外的方法(没有像_method参数那样的黑客)。 一个非常实际的原因是,有时候,“网页加速器”浏览器附加组件会在页面中预取链接; 如果GET链接启动了一个改变动作,则可能会错误地修改用户或资源状态。

也就是说,你可以设置按钮的样式,就像链接一样; 我使用类似下面的东西来做得很好。 它假设一个适当的CSS重置边缘和填充和所有好东西被填充。

 input.restlink { border: 0; background: #fff; color: #236cb0; cursor: pointer; } input.restlink:hover { text-decoration: underline; } 

有了这样的规则,你可以使用<%=button_to "Yay button", something_path, :method => :post %> ,它看起来和行为就像一个链接就好了。

如果你使用的是jQuery,你也可以这样做:

  <%= form_tag( user_free_dates_path(:user_id => @user.id), :remote => true ) do |f| %> <%= hidden_field_tag :date, current_day.to_s(:short_db) %> <%= link_to "Allow", "#", :class => "submit"%> <% end %> // in application.js $("form a.submit").live("click", function(){ $(this).closest("form").submit(); return false; }); 

这个想法是你在视图中构建一个“真实”的表单,没有提交按钮。 然后,带有“submit”类的link_to将触发表单提交。 最终结果看起来像一个链接,但实际上它是一个表单。 我确信使用Prototype有类似的方法。

在我的示例中, user_free_dates_path(:user_id => @ user.id)只是一个路径助手,而hidden_​​field_tag是我在POST请求中传递的参数之一。

如果你不反对使用jQuery和一些ajax’n,我有一篇博文来介绍这一点。

如果您想要高级概述,请参阅以下基本信息。

添加这个你的布局:

 <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> 

此代码将auth令牌添加到响应中。 通过这种方式,JS可以将其提取并提交给服务器。

接下来我们拦截application.js中的任何ajax调用:

 function isPost(requestType) { return requestType.toLowerCase() == 'post'; } $(document).ajaxSend(function(event, xhr, settings) { if (isPost(settings.type)) { settings.data = (settings.data ? settings.data + "&" : "") + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN ); } xhr.setRequestHeader("Accept", "text/javascript, application/javascript"); }); 

将其添加到应用程序控制器:

 before_filter :correct_safari_and_ie_accept_headers after_filter :set_xhr_flash protected def set_xhr_flash flash.discard if request.xhr? end def correct_safari_and_ie_accept_headers ajax_request_types = ['text/javascript', 'application/json', 'text/xml'] request.accepts.sort!{ |x, y| ajax_request_types.include?(y.to_s) ? 1 : -1 } if request.xhr? end 

在你看来:

 <%= link_to "Delete", delete_product_path(product), :class => 'delete' %> 

回到application.js:

 $('a.delete').live('click', function(event) { if(event.button != 0) { return true; } $.post(link.attr('href').substring(0, link.attr('href').indexOf('/delete')), { _method: "delete" }); return false; }); 

此示例执行删除操作,但处理post或放置的过程确实相同。 博客文章有一个完整的示例应用程序来演示这一点。

从这些答案/来源中大量借用

获得一个看起来像链接但function类似按钮的按钮。 在我的app / helpers / application_helper.rb中:

 def button_as_link(action, link_text, hiddens) capture do form_tag(action, :class => 'button_as_link') do hiddens.each { |k, v| concat hidden_field_tag(k.to_sym, v) } concat submit_tag(link_text) end end end 

CSS:

 form.button_as_link { display: inline; } /* See https://stackoverflow.com/a/12642009/326979 */ form.button_as_link input[type="submit"], form.button_as_link input[type="submit"]:focus, form.button_as_link input[type="submit"]:active { /* Remove all decorations to look like normal text */ background: none; border: none; display: inline; font: inherit; margin: 0; padding: 0; outline: none; outline-offset: 0; /* Additional styles to look like a link */ color: blue; cursor: pointer; text-decoration: underline; } /* Remove extra space inside buttons in Firefox */ form.button_as_link input[type="submit"]::-moz-focus-inner { border: none; padding: 0; } 

最后在我的模板中,我调用了button_as_link函数:

 button_as_link('/some/path',{:key1=>:val1,:key2=>:val2}, 'text to display')