嵌套的 content_tags 转义内部 html.. 为什么?

2024-04-08

因此,如果我循环并创建 li/a 标签的集合,我会按预期得到......这些标签的数组:

(1..5).to_a.map do
  content_tag(:li) do
    link_to("boo", "www.boohoo.com")
  end
end

=> ["<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>", "<li><a href=\"www.boohoo.com\">boo</a></li>"] 

我对它们调用 join 并得到一个预期的字符串......

(1..5).to_a.map do
  content_tag(:li) do
    link_to("boo", "www.boohoo.com")
  end
end.join

=> "<li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li>" 

但是,如果我将这一层嵌套在 ol 标记中更深......

content_tag(:ol) do
  (1..5).to_a.map do
    content_tag(:li) { link_to("boo", "www.boohoo.com") } 
  end.join
end

 => "<ol>&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;</ol>"

我摆脱了内部 html 的疯狂!!!

查看rails源代码时:

  def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
    if block_given?
      options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
      content_tag_string(name, capture(&block), options, escape)
    else
      content_tag_string(name, content_or_options_with_block, options, escape)
    end
  end

  private

    def content_tag_string(name, content, options, escape = true)
      tag_options = tag_options(options, escape) if options
      "<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
    end

它看起来像是我可以这样做: content_tag(:li, nil, nil, false) 并且不让它转义内容..但是:

content_tag(:ol, nil, nil, false) do
  (1..5).to_a.map do
    content_tag(:li, nil, nil, false) do 
      link_to("boo", "www.boohoo.com")
    end
  end.join
end
=> "<ol>&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;www.boohoo.com&quot;&gt;boo&lt;/a&gt;&lt;/li&gt;</ol>"

我仍然患有不必要的 html_escape 综合症......

所以我知道避免这种情况的唯一方法是:

content_tag(:ol) do
  (1..5).to_a.map do
    content_tag(:li) do 
      link_to("boo", "www.boohoo.com")
    end
  end.join.html_safe
end

=> "<ol><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li><li><a href=\"www.boohoo.com\">boo</a></li></ol>"

但是.. 为什么会发生这种情况?


发生这种情况是因为在 Rails 3 中引入了 SafeBuffer 类,它包装了 String 类并覆盖了调用 concat 时会发生的默认转义。

在您的情况下, content_tag(:li) 正在输出正确的 SafeBuffer,但 Array#join 不理解 SafeBuffers,只是输出一个字符串。然后使用 String 而不是 SafeBuffer 来调用 content_tag(:ol) 并将其转义。因此,它与嵌套关系不大,而与返回 String 而不是 SafeBuffer 的 join 有关。

在字符串上调用 html_safe、将字符串传递给 raw 或将数组传递给 safe_join 都会返回一个正确的 SafeBuffer 并防止外部 content_tag 转义它。

现在,在将 false 传递给转义参数的情况下,当您将块传递给内容标记时,这不起作用,因为它正在调用capture(&block)ActionView::Helpers::CaptureHelper 用于拉入模板,或者您的情况 join 的输出值,然后导致它调用html_escape在字符串进入之前content_tag_string method.

  # action_view/helpers/tag_helper.rb
  def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
    if block_given?
      options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
      # capture(&block) escapes the string from join before being passed
      content_tag_string(name, capture(&block), options, escape)
    else
      content_tag_string(name, content_or_options_with_block, options, escape)
    end
  end

  # action_view/helpers/capture_helper.rb
  def capture(*args)
    value = nil
    buffer = with_output_buffer { value = yield(*args) }
    if string = buffer.presence || value and string.is_a?(String)
      ERB::Util.html_escape string
    end
  end

由于这里的 value 是 join 的返回值,而 join 返回一个 String,因此它会在 content_tag 代码通过自己的转义到达它之前调用 html_escape 。

一些有兴趣的参考链接

https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/capture_helper.rb https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/capture_helper.rb

https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/tag_helper.rb https://github.com/rails/rails/blob/v3.1.0/actionpack/lib/action_view/helpers/tag_helper.rb

http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/ http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/

http://railsdispatch.com/posts/security http://railsdispatch.com/posts/security

Edit

处理这个问题的另一种方法是执行map/reduce而不是map/join,因为如果reduce没有传递参数,它将使用第一个元素并使用该对象运行给定的操作,在map content_tag的情况下将调用SafeBuffer 上的操作。

content_tag(:ol) do
  (1..5).to_a.map do
    content_tag(:li) do
      link_to(...)
    end
  end.reduce(:<<)
  # Will concat using the SafeBuffer instead of String with join
end

作为单行

content_tag(:ul) { collection.map {|item| content_tag(:li) { link_to(...) }}.reduce(:<<) }

添加一点元香料来清理东西

ul_tag { collection.map_reduce(:<<) {|item| li_link_to(...) } }

谁需要 html_safe...这是 Ruby!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌套的 content_tags 转义内部 html.. 为什么? 的相关文章

随机推荐