Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

render_to_string changes content-type for subsequent render #14173

Open
luckyruby opened this Issue Feb 24, 2014 · 32 comments

Comments

Projects
None yet

In Rails 3.2.16:

  def locations
    @locations = Location.all
    stream = render_to_string(template: "locations/index", formats: [:xml])
    render nothing: true
  end

I would expect a blank page when I hit this controller action from my browser but I get an XML parsing error in Firefox instead. It seems that render_to_string is changing the content-type response header. Is that expected behavior?

Member

senny commented Feb 24, 2014

@luckyruby is this also happening for 4.0 or 4.1? We no longer provide bug fixes for 3.2. Also please provide a way to reproduce the error, this will help to resolve it more quickly. You can use this script as a foundation.

I can confirm the same issue after upgrading an app from 3.2 to 4.0.3, I've isolated it in a demo app:
https://github.com/manufaktor/render_to_string_demo

The page renders in text/plain instead of text/html.

Member

senny commented Mar 4, 2014

Side effects of render_to_string are causing other issues as well #11623.

@manufaktor thanks for the sample application. Do you think the issue is reproducible in a minimal setup like this script ?

@senny senny removed the needs feedback label Mar 4, 2014

Member

senny commented Mar 4, 2014

@manufaktor does the test-case in the gist fail for you? I ran it against a couple different versions of Rails 4 and it seemed to work...

@senny yes, fails for me

Run options: --seed 8574

# Running tests:

D, [2014-03-04T16:11:17.700701 #22937] DEBUG -- : 
D, [2014-03-04T16:11:17.700822 #22937] DEBUG -- : 
I, [2014-03-04T16:11:17.701837 #22937]  INFO -- : Started GET "/" for 127.0.0.1 at 2014-03-04 16:11:17 +0100
F

Finished tests in 0.126765s, 7.8886 tests/s, 15.7772 assertions/s.

  1) Failure:
BugTest#test_returns_success [test.rb:47]:
Expected: "text/html; charset=utf-8"
  Actual: "text/plain; charset=utf-8"

1 tests, 2 assertions, 1 failures, 0 errors, 0 skips

I'm on ruby-2.0.0-p353

Member

senny commented Mar 4, 2014

This is strange indeed. I also downloaded your sample application and get:

ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-darwin13.0.0]:

Hi from index.html.erb

My gems are installed locally using a custom BUNDLE_PATH. What operating system are you running on?

Im on Mac OS 10.9 using rbenv
My testcase also fails with rails 3.2.17

Contributor

maletor commented Mar 11, 2014

Same issue here. Any resolution?

I think you can work around it by setting the content type explicitly of the following render:

render :partial => 'stuff', :content_type => 'text/html'
Contributor

maletor commented Mar 11, 2014

Ya, I ended up doing just that (except with respond_with). This is a downer, especially with no bug fixes coming into 3-2-stable. Is this fixed in 4?

@rails-bot rails-bot added the stale label Mar 20, 2015

This issue has been automatically marked as stale because it has not been commented on for at least
three months.

The resources of the Rails team are limited, and so we are asking for your help.

If you can still reproduce this error on the 4-2-stable, 4-1-stable branches or on master,
please reply with all of the information you have about it in order to keep the issue open.

Thank you for all your contributions.

I can reproduce the failing test on Rails 4.2.1 (Ruby 2.1.5) and Rails master (Ruby 2.2.1).

@senny senny added pinned and removed stale labels Mar 23, 2015

Member

senny commented Mar 23, 2015

@manufaktor thank you for confirming. I removed the stale label and added pinned.

Happening here too, in 4.2. I'm rendering some jbuilder json like so: @json = render_to_string template: "browse/_json", layout: false

And it causes the whole page to render with content-type application/json.

Please let me know if you need more input.

So I tried to debug this, but this was my first time digging into rails internals and I got a bit lost.

What I found is that the PartialRenderer does a conditional assignment of rendered_format= onto the LookupContext, which is shared between render_to_string and render (it's per request I think?). I guess removing the conditional assignment in actionview/lib/action_view/renderer/partial_renderer.rb:299 would fix the test I've provided.

I'm not sure if this is the best way though, because every partial render would have to figure out rendered_format again, which I believe is not necessary unless you are calling from render_to_string.

I guess it would also be possible to set lookup_context.rendered_format = nil when render_to_string is done. It would then be freshly assigned again when calling `render.

In 4.1.8, render_to_string is causing the response to an xhr request to have the header Content-Type:text/html.

Removing the call to render_to_string and the header normalizes to Content-Type:text/javascript.

Contributor

repinel commented Jun 18, 2015

FWIW, minimal example of the issue using Rails 5 master: https://gist.github.com/repinel/92810d0d47ec23543016

Contributor

repinel commented Jun 18, 2015

Since render_to_string reuses most of the render_template features, it seems to me that the rendered_format is being set here.

@senny Could we provide some option around here indicating that the result will be rendered as string and there is no need to set the rendered_format? Any other suggestion?

Contributor

jcoleman commented Jun 26, 2015

This is also linked to #14693

Edit: Apparently these used to be linked, but I think the above has been fixed.

maclover7 added a commit to maclover7/rails that referenced this issue Apr 21, 2016

render_to_string changes content-type for subsequent render
This uncaches `@lookup_context.rendered_format`, since it was causing
bugs like #14173. This patch includes a regression test :)
Member

maclover7 commented Apr 21, 2016

Been a long time coming, but opened up PR #24665 to solve this. :)

lookup_context.rendered_format = nil when render_to_string is done

It works for Rails 4.2.6

Any follow up on how this is progressing?

ramaboo commented Apr 11, 2017

I am having this issue as well! Fix would be awesome!

rewolf commented May 17, 2017

Also still seeing this on 5.1.0. If I use render_to_string somewhere in an action then the overall format is thought to be json when the following render occurs for the action

kevmimcc commented Jun 7, 2017

Any work around for this? I'm still getting this with:
Rails 5.0.2
Ruby 2.4.0p0

GermanDZ commented Sep 5, 2017

I've used this trick in the past to get rid of this problem:

def some_action
  original_formats = formats.dup

  # This is the render under a new format
  info_as_json = JSON.parse(render_to_string(formats: [:json], layout: false))

  # This is a hack to force to the controller to render the right format.
  # The previous render_to_string seems to be changing the format for the whole request
  render_to_string(template: "module_name/controller/action", formats: original_formats, layout: false)

  .
  .
  the rest of your action (probably nothing, the action will render the default template, in this case `some_action`
end
Contributor

mrj commented Sep 19, 2017

I work around this bug by explicitly setting the content_type for the action template render at the top of any controller action that calls render_to_string.

For example, if you're rendering a JavaScript action template, add response.content_type = 'text/javascript' to the top of the action to prevent render_to_string of HTML templates during the action from setting the final content_type to text/html, which stops the JavaScript from executing.

Good to know, but in my case I'm responding with text or json (another kind of text :) ) with the same code, the view is in charge of the formatting. So, I can't "force" a single format in the action method.

Contributor

mrj commented Sep 20, 2017

If you have multiple render templates you can add

respond_to do |format|
  format.html
  format.json
end

or just

respond_to { |format| format.any(:html, :json) }

to the bottom of the action, since this also sets the correct content_type from the request.

I've verified that adding

respond_to { |format| format.js }

works in my case.

jplaut commented Oct 25, 2017

Still getting this with Rails 5.1.4

  respond_to do |format|
    format.html
  end

worked for me as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment