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

5
在我的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中不再可用。

实际上,erase_render_results 只是将标志设置为 false 并擦除 response_body - 在 render_to_string 的源代码中,内容类型明确设置为 nil。这似乎更有可能是您遇到的问题的原因。 - Brett Bender
3个回答

2

看起来这可能是Rails 3中的一个bug(至少与2.3.x render_to_string的行为相比)。在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.3版的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。可能content-type、response_body等在其他地方处理,这可能是个误导,但我的第一反应是在实际渲染之前,在你的render_to_string后设置
response.headers['Content-Type'] = 'text/html'

2
实际上,使用一个视图文件和两个渲染调用不是Rails的正确做法:视图通常是发送到客户端的内容,而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

请查看Builder文档,了解其工作原理。
Builder的另一个特性是可以使用<<将XML内容附加到缓冲区中,因此任何IO流都可以使用。根据您向其他服务器发送内容的方式,您可以将其整合在一起。
当然,这可能会变得非常混乱和冗长,这就是为什么您需要将这个功能模块封装在另一个类中或者作为模型中的一个方法。

1
问题在于XML数据还必须作为普通视图可用,因此按照你的建议意味着复制代码(在我的情况下)。尽管如此,这就是我做的事情,谢谢。 - Daniel Rikowski

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

希望这可以帮到您。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接