【问题标题】:making POST request in rails with hyperlinks使用超链接在 Rails 中发出 POST 请求
【发布时间】:2011-03-29 23:58:20
【问题描述】:

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

【问题讨论】:

    标签: ruby-on-rails forms post hyperlink


    【解决方案1】:

    你有很多选择。

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

    顺便说一句,您如何仅使用“href”属性发出“发布”请求?我认为形式是必须的。

    【讨论】:

    • Rails 允许您指定 _method=(PUT|POST|DELETE) 查询参数,它会将请求解释为使用该方法发出的。主要是支持PUT/DELETE 没有完整的浏览器支持。
    • 我真的不知道禁用 AT 检查的安全隐患。不过,我可以尝试将其添加为请求参数。但是 POST 是在与创建此视图的控制器不同的控制器上完成的。那应该有关系吗?这个令牌的范围是什么?
    • @manu1001 据我了解,AT 是为特定会话创建的。所以,它应该工作。无论如何,很容易尝试:'my_url?authenticity_token='
    【解决方案2】:

    从技术上讲,您应该为任何不是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 %>,它的外观和行为就像一个链接就好了。

    【讨论】:

    • 我考虑过这一点,但没有成功,因为当您将鼠标按下按钮时,仍然存在令人讨厌的推动效果;在某些浏览器上用虚线环绕。
    • 通过适当的 CSS 重置,这两种情况都不是。您可以为显示脏矩形的浏览器设置outline: none
    【解决方案3】:

    如果你使用 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 => @user.id) 只是一个路径助手,而 hidden_​​field_tag 是我的参数之一传入我的 POST 请求。

    【讨论】:

      【解决方案4】:

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

      如果您想获得高级概述,这里有一些基本信息。

      将此添加到您的布局中:

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

      此代码将身份验证令牌添加到响应中。这样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;
      });
      

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

      【讨论】:

        【解决方案5】:

        大量借鉴这些答案/来源

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-10
          • 2021-12-19
          • 2012-03-04
          • 2017-02-04
          相关资源
          最近更新 更多