在带有超链接的rails中发出POST请求
我需要在页面上有一堆链接,每个链接都会向不同的控制器发出POST。 但是当我使用普通链接时,我得到一个ActionController :: InvalidAuthenticityToken错误。 我理解这是因为缺少authenticity_token值。 但我不想使用表单来进行POST,因为我希望它们是链接而不是按钮。 事实是,我想完全控制链接和按钮的样式,只是不要为我做。 做这些事情的标准方法是什么?
你有很多选择。
- 禁用AT检查:
protect_from_forgery :only => []
- 将onclick处理程序分配给链接并使用javascript提交隐藏的表单。
- 生成视图时抓取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或放置的过程确实相同。 博客文章有一个完整的示例应用程序来演示这一点。
从这些答案/来源中大量借用
- https://stackoverflow.com/a/3486036/326979
- https://stackoverflow.com/a/12642009/326979
- http://juliankniephoff.wordpress.com/2011/03/23/using-form_for-in-helper-methods/
获得一个看起来像链接但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')