代码之家  ›  专栏  ›  技术社区  ›  jacob

Ruby on Rails表单页面缓存,包括真实性标记

  •  18
  • jacob  · 技术社区  · 15 年前

    我有一个简单的RubyonRails表单,其中包含一个真实的标记。不幸的是,我错过了当您页面缓存此页面时,真实性令牌将变为无效。不过,我很高兴我弄明白了。

    在这种情况下,如何解决缓存问题?

    5 回复  |  直到 12 年前
        1
  •  22
  •   Mike Trpcic    15 年前

    正如Matchu发布的那样,您可以从 this post (他发布了同样的链接,但也是通过我的谷歌搜索找到的)。这增加了对javascript的依赖,这可能是您想要的,也可能不是您想要的。

    或者,你可以看看 Fragment Caching . 这允许您缓存某些 部分 但仍然生成动态部分(例如带有真实性标记的表单)。使用这种技术,您可以缓存页面的其余部分,但为每个请求生成一个新表单。

    最后一个解决方案(但最不利的)是禁用该特定操作的真实性令牌。您可以通过将以下内容添加到生成该表单的控制器的开头来完成此操作:

    protect_from_forgery :except => [:your_action]
    

    您还可以通过在开始处添加以下内容来关闭对整个控制器的“保护”功能:

    skip_before_filter :verify_authenticity_token
    
        2
  •  1
  •   Matchu    15 年前

    这似乎不是一个很好解决的问题。 Point two on this blog post 描述如何使用jquery完成任务,但这引入了一个javascript依赖项。我想,要权衡一下你的选择。

        3
  •  1
  •   Niklas Hofer    13 年前

    您可以在缓存标记中呈现自定义标记,并将其替换为在每个请求上呈现的表单。

    module CacheHelper
      # Our FORM is deeply nested in the CACHED_PARTIAl, which we
      # cache. It must be rendered on every request because of its
      # authenticity_token by protect_from_forgery. Instead of splitting up the
      # cache in multiple fragments, we replace a special tag with the custom
      # form.
      def cache_with_bla_form(resource, &block)
        form = nil
        doc = Nokogiri::HTML::DocumentFragment.parse( capture { cache("your_cache_key",&block) } )
        doc.css('uncachable_form').each do |element|
          form ||= render(:partial => 'uncachable_form', :resource => resource)
          element.replace form
        end
        doc.to_html
      end
    end
    

    在您的视图中,您只需呈现一个空的不可缓存的_Form标记。

    <%- cache_with_bla_form resource do %>
      # cachable stuff..
      <uncachable_form />
      # more cachable stuff
    <%- end %>
    

    是的,这可以被视为一种黑客行为,但是它不会放松伪造保护,不需要JS,并且只会稍微降低缓存带来的性能增益。我认为有人实现了与机架中间件类似的模式。

        4
  •  1
  •   Nick Urban    12 年前

    我遵循了Niklas Hofer的一般解决方案,但我发现他的实现与Rails缓存助手的确切语义不匹配。也就是说,它试图从帮助程序返回缓存的HTML,而不是使用 safe_concat ,这是Rails助手所做的。

    Rails助手的用法如下:

    - cache do
      = something
    

    然而他的解决方案需要这种语法:

    = cache_with_updated_csrf do
      = something
    

    为了保持一致性,我希望这些工作方式相同。因此,我使用了以下语法:

    - cache_form do
      = something
    

    这是我的实现。当缓存被禁用时,它也将跳过缓存,就像Rails助手那样。

    module CacheHelper
      # Cache a form with a fresh CSRF
      def cache_form(name = {}, options = nil, &block)
        if controller.perform_caching
          fragment = fragment_for(name, options, &block)
    
          fragment_with_fresh_csrf = Nokogiri::HTML::DocumentFragment.parse( fragment ).tap do |doc|
            doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
          end.to_html
    
          safe_concat fragment_with_fresh_csrf
        else
          yield
        end
    
        nil
      end
    end
    
        5
  •  0
  •   Community CDub    8 年前

    作为一个更通用的解决方案,您还可以用当前的真实性标记替换所有缓存的真实性标记:

    module CacheHelper
      def cache_with_updated_csrf(*a, &block)
        Nokogiri::HTML::DocumentFragment.parse( capture { cache(*a,&block) } ).tap do |doc|
          doc.css("input[name=#{request_forgery_protection_token}]").each { |e| e['value'] = form_authenticity_token }
        end.to_html.html_safe
      end
    end
    

    和使用 = cache_with_updated_csrf do 而不是 - cache do 在你看来。尊敬的 Bernard Potocki 为了这个想法。