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

时间:2010-08-15 02:58:46

标签: ruby-on-rails forms post hyperlink

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

5 个答案:

答案 0 :(得分:2)

你有很多选择。

  1. 禁用AT检查:protect_from_forgery :only => []
  2. 将onclick处理程序分配给链接,并使用javascript提交隐藏的表单。
  3. 在生成视图时抓取AT并将其添加为请求参数。
  4. 顺便说一下,你究竟如何只使用'href'属性来'发布'请求?我认为形式是必须的。

答案 1 :(得分:1)

从技术上讲,你应该使用按钮和表单来处理非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 %>,它看起来和行为就像一个链接就好了。

答案 2 :(得分:1)

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

<!--  in your view --> 
<%= 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 =&gt; @ user.id)只是路径助手,而 hidden_​​field_tag 是我参与的参数之一传递我的POST请求。

答案 3 :(得分:0)

如果你不反对使用jQuery和一些ajax'n,我有一个blog post来涵盖这个。

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

添加你的布局:

<%= 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;
});

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

答案 4 :(得分:0)

大量借用这些答案/来源

获取一个看起来像链接但功能类似按钮的按钮。在我的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')