如何在使用render_to_string后正常渲染视图?

时间:2011-01-13 19:44:39

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

在我的Rails应用程序中,我有一个使用XML Builder模板(rxml)模板和render_to_string创建XML文档的操作。 XML文档被转发到后端服务器。

创建XML文档后,我想向浏览器发送普通的HTML响应,但不知何故Rails记住第一次调用render_to_string

例如:

  • Rails无法找到默认视图show.html.erb,因为它会查找show.rxml
  • 只需将render 'mycontroller/show.html.erb'放在我的操作处理程序的底部,就可以使Rails找到模板,但浏览器不起作用,因为响应标头的内容类型为text/xml

有没有办法在没有“污染”实际浏览器响应的情况下使用render_to_string

编辑:似乎在Rails 2中erase_render_results可以解决问题,但在Rails 3中它已不再可用。

3 个答案:

答案 0 :(得分:2)

好像这可能是rails 3中的一个错误(至少与2.3.x render_to_string的行为相比)。在source for 2.3.8中,他们明确采取额外步骤来重置content_type并将响应主体设置为nil(除其他外)。

def render_to_string
  ...
ensure
  response.content_type = nil
  erase_render_results
  reset_variables_added_to_assigns
end

但在3.0 AbstractController::Rendering

的来源中
def render_to_string(*args, &block)
  options = _normalize_args(*args, &block)
  _normalize_options(options)
  render_to_body(options)
end

您可以看到没有显式重置变量,render_to_body只返回view_context.render。内容类型,response_body等可能在别处处理,这是一个红色的鲱鱼,但我的第一直觉是设置

response.headers['Content-Type'] = 'text/html'

在实际呈现之前的render_to_string

答案 1 :(得分:2)

实用的答案是,使用视图文件和两次渲染渲染是Not The Rails Way:视图通常是发送到客户端的东西,而ActionPack被设计为以这种方式工作。

那就是说,实现你想要做的事情是一种简单的方法。您可以直接使用Builder :: XmlMarkup将XML生成为字符串:

,而不是使用ActionView
def action_in_controller
  buffer = ""
  xml = Builder::XmlMarkup.new(buffer)

  # build your XML - essentially copy your view.xml.builder file here 
  xml.element("value")
  xml.element("value")

  # send the contents of buffer to your 3rd server

  # allow your controller to render your view normally
end

查看the Builder documentation,看看它是如何运作的。

您可以利用的Builder的另一个功能是使用<<将XML内容附加到缓冲区,因此可以使用任何IO流。根据您将内容发送到其他服务器的方式,您可以很好地完成所有内容。

当然,这可能会变得非常混乱和冗长,这就是为什么您想要将这些功能封装在另一个类中,或者作为模型中的方法。

答案 2 :(得分:2)

在迁移actionwebservice gem时遇到了同样的错误。在他们的代码中,他们通过调用函数erase_render_results来绕过双重渲染异常。

rails3中不再提供此功能。幸运的是,修复非常简单(但我花了一些时间才找到)。

在actionwebservice内部,在控制器内调用以下函数以允许第二次渲染:

def reset_invocation_response
  erase_render_results
  response.instance_variable_set :@header, Rack::Utils::HeaderHash.new(::ActionController::Response::DEFAULT_HEADERS.merge("cookie" => []))
end

要在rails3中使其工作,您只需写:

def reset_invocation_response
  self.instance_variable_set(:@_response_body, nil)
  response.instance_variable_set :@header, Rack::Utils::HeaderHash.new("cookie" => [], 'Content-Type' => 'text/html')
end

希望这有帮助。