rails: Including ActionView::Rendering in API controller without jbuilder breaks render :json

Including ActionView::Rendering in a controller of an API-only application breaks render :json, resulting in an ActionView::MissingTemplate error. This is similar to #25183, except that the error occurs without using jbuilder.

Steps to reproduce

  1. Use rails new --api to create a Rails app with jbuilder disabled by default.
  2. Include ActionView::Rendering in a controller subclassed from ActionController::API, then render :json:
class TestController < ActionController::API
  include ActionView::Rendering

  def index
    render json: {page: "Home"}
  end
end

Full example failing test

Expected behavior

JSON response should be returned without error. No template should be used.

Actual behavior

ActionView::MissingTemplate is raised.

System configuration

Rails version: 5.0.0.1 (used rails new --api)

Ruby version: 2.3.1

About this issue

  • Original URL
  • State: open
  • Created 8 years ago
  • Reactions: 10
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Sorry I’m late, Happy birthday to this issue!. Still happening on rails 5.1

Still an issue, rails 5.2…

I have this issue, too.

The draper gem requires a view context to be available (thus manually includes ActionView::Rendering when Rails is in API mode), and the responders gem checks for the presence of ActionView::Rendering to determine whether it should use API or default behavior for returning a response.

As far as I can tell, ActionView::Rendering is not included in API mode so that the view layer is skipped (it’s not needed), so this should stay that way. Having the view_context (and thus certain helpers, right?) available in API mode probably makes sense, though.

If I am thinking this through correctly, would this be a proper solution then?

  • Split ActionView::Rendering into two modules, one providing the view context, and the other one taking care of rendering with the view stack.
  • ActionController::Base would include both.
  • ActionController::API would only include the former.

@st0012 thanks for looking into this. Any reason not to simply do:

alias_method :_orig_render_to_body, :render_to_body
include ActionView::Rendering
alias_method :render_to_body, :_orig_render_to_body

I’ve add the same issue with Rails 5.0.4 and jbuilder 2.7.0. I have a hybrid web/API app.

I wanted to switch my API controllers from inheriting from ActionController::Base to ActionController::API

    class APIController < ActionController::API
      include ActionView::Rendering
    end

This unfortunately triggered a different issue with render json: {} that seems to ask for an obviously non existent template.

I thought this had been fixed by https://github.com/rails/rails/pull/24178 but apparently not.

The temporary solution was https://github.com/rails/rails/issues/27211#issuecomment-264392054

The other suggested solution, aliasing, still breaks https://github.com/rails/rails/issues/27211#issuecomment-264966302

@jpettettphaxio The cause of this error is that ActionView::Rendering override render_to_body to

    def render_to_body(options = {})
      _process_options(options)
      _render_template(options)
    end

The overwrote method is ActionController::Renderers#render_to_body

    def render_to_body(options)
      _render_to_body_with_renderer(options) || super
    end

However, re-include ActionController::Renderers won’t work. But if you manually override render_to_body in your controller will work, which would be like

class TestController < ActionController::API

  private

  def render_to_body(options)
    _render_to_body_with_renderer(options) || super
  end
end

@NaixSpirit this work-around is no longer necessary with the latest release of Draper.

And/or since ActionController::Base includes ActionView::Rendering without breaking render :json, is there another module that needs to be included in a ActionController::API subclass to “fix” render_to_body after including ActionView::Rendering?

@jpettettphaxio I just want to confirm, you are only including ActionView::Rendering because you want access to the controller’s view_context, right?