2011-01-13 18 views
5

En mi aplicación Rails tengo una acción que crea un documento XML utilizando una plantilla de Plantilla de Generador XML (rxml) y render_to_string. El documento XML se reenvía a un servidor backend.¿Cómo renderizar una vista normalmente después de usar render_to_string?

Después de crear el documento XML, quiero enviar una respuesta HTML normal al navegador, pero de alguna manera Rails está recordando la primera llamada al render_to_string.

Por ejemplo:

  • Carriles no puede encontrar la vista predeterminada show.html.erb porque busca un show.rxml.
  • Simplemente poniendo un render 'mycontroller/show.html.erb' en la parte inferior de mi controlador de acción hace que Rails encuentre la plantilla, pero el navegador no funciona porque el tipo de contenido del encabezado de respuesta es text/xml.

¿Hay alguna forma de utilizar render_to_string sin "manchar" la respuesta del navegador real?

EDIT: Parece que en Rails 2 erase_render_results haría el truco, pero en Rails 3 ya no está disponible.

+0

En realidad, 'erase_render_results' solo establece una bandera en falso y borra el response_body - en la fuente de render_to_string, el tipo de contenido se establece explícitamente en nil. Parece más probable que se deba al problema que has encontrado. –

Respuesta

2

La respuesta pragmática es que usar un archivo de vista y dos llamadas a renderizar no es The Rails Way: las vistas son generalmente algo que se envía al cliente, y ActionPack está diseñado para funcionar de esa manera.

Dicho esto, hay una manera fácil de lograr lo que estás tratando de hacer. En lugar de utilizar ActionView, se puede usar Constructor :: XmlMarkup directamente para generar el código XML como una cadena:

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 

Tenga una mirada en the Builder documentation para ver cómo funciona.

La otra característica de Builder que puede aprovechar es el hecho de que el contenido XML se anexa al almacenamiento intermedio utilizando <<, por lo que se puede usar cualquier flujo de IO. Dependiendo de cómo esté enviando contenido al otro servidor, podría envolverlo todo muy bien.

Por supuesto, esto podría terminar muy desordenado y largo, por lo que querría encapsular este poco de funcionalidad en otra clase, o como un método en su modelo.

+1

El problema es que los datos XML también deben estar disponibles como una vista normal, por lo que seguir tu consejo significa duplicar el código (en mi caso) Sin embargo, esto es lo que hice, gracias. –

2

Parece que esto puede ser un error en los rieles 3 (al menos en comparación con el comportamiento de 2.3.x render_to_string). En el source for 2.3.8, claramente toman medidas adicionales para restablecer content_type y establecer el cuerpo de respuesta en cero (entre otras cosas).

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

pero en la fuente 3.0.3 para AbstractController::Rendering

def render_to_string(*args, &block) 
    options = _normalize_args(*args, &block) 
    _normalize_options(options) 
    render_to_body(options) 
end 

Se puede ver que no hay reposición explícita de variables, render_to_body sólo devuelve view_context.render. Es posible que el tipo de contenido, response_body, etc se manejan en otros lugares y esto es una cortina de humo, pero mi primer instinto sería establecer

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

después de su render_to_string antes de que realmente la representación.

2

En la migración de la gema actionwebservice me encontré con el mismo error. En su código evitan la excepción de doble representación llamando a la función erase_render_results.

Esta función no está disponible en rails3. Afortunadamente la solución es bastante fácil (pero tardé un poco en encontrarla).

Dentro actionwebservice la siguiente función se llamaba en el interior de un controlador para permitir una segunda imagen:

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

para hacer este trabajo en rails3, sólo hay que escribir:

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 

Espero que esto ayude .

+0

+1 extremadamente útil, ¡obtienes 5 internets! –

Cuestiones relacionadas