【问题标题】:nested content_tags escape inner html.. why?嵌套的 content_tags 转义内部 html .. 为什么?
【发布时间】:2011-09-26 23:22:14
【问题描述】:

所以,如果我循环并创建一个 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>"

但是.. 为什么会这样?

【问题讨论】:

  • 这只是代表目的还是某些逻辑行为需要服务器端来填充这个块?

标签: ruby-on-rails ruby-on-rails-3


【解决方案1】:

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

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

在 String 上调用 html_safe、将 String 传递给 raw 或将数组传递给 safe_join 都将返回正确的 SafeBuffer 并防止外部 content_tag 转义。

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

  # 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/tag_helper.rb

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

http://railsdispatch.com/posts/security

编辑

另一种处理方法是使用 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!

【讨论】:

  • 不错的答案!你是如何到达 map_reduce 的?你是自己做的方法还是用 Rails(Ruby) 构建的?
  • 我只是编造的,它是猴子修补在 Enumerable 上的,简直就是,def map_reduce(op, &block) self.map(&block).reduce(op) end
【解决方案2】:

如果你使用safe_join会发生什么?

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

还是只使用 raw?

content_tag(ol) do
  1.upto(5) {
    raw content_tag(:li) { link_to 'boo', 'www.boohoo.com' }
    #  or maybe
    #    raw content_tag(:li) { raw link_to('boo', 'www.boohoo.com') } 
  }
end

【讨论】:

    【解决方案3】:

    不是肯定的,但我认为 html 转义发生在每个“层”(因为没有更好的术语;每次迭代)——我的意思是在内部块级别(1..5)......。然后在外部块级别 (content_tag(:ol) 做 ...

    【讨论】:

    • 我不这么认为..它发生在调用 "join"..content_tag(:div) { content_tag(:ol) } 输出 "
        " ... 其中 content_tag(:div) { [content_tag(:ol)].join } 输出 "
        <ol></ol>
        "
      猜你喜欢
      • 1970-01-01
      • 2012-06-06
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      相关资源
      最近更新 更多