在我的Rails应用程序中,我有一个使用XML Builder模板(rxml
)模板和render_to_string
创建XML文档的操作。 XML文档被转发到后端服务器。
创建XML文档后,我想向浏览器发送普通的HTML响应,但不知何故Rails记住第一次调用render_to_string
。
例如:
show.html.erb
,因为它会查找show.rxml
。render 'mycontroller/show.html.erb'
放在我的操作处理程序的底部,就可以使Rails找到模板,但浏览器不起作用,因为响应标头的内容类型为text/xml
。有没有办法在没有“污染”实际浏览器响应的情况下使用render_to_string
?
编辑:似乎在Rails 2中erase_render_results
可以解决问题,但在Rails 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生成为字符串:
,而不是使用ActionViewdef 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
希望这有帮助。