Fix Rails Converting Backbone JSON Requests to text/html

During a recent project with BackboneJs and Rails, i noticed some very strange behavior. When my controller had an implicit render to Backbone's JSON requests, Rails was throwing an error.

ActionView::MissingTemplate (Missing template users/index, base/index with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :arb]}.

Rails is complaining that no template exists for the response format of HTML. Well this is unexpected. The request, which comes from Backbone, should be expecting JSON as a return type. But for some reason, Rails is attempting to return HTML. After some investigation, you find that the problem is lying in the request object itself.

p request.headers[ 'HTTP_ACCEPT' ]  
# > "application/json, text/javascript, */*; q=0.01"

p request.format.to_s  
# > "text/html"

Now, request.headers represents the raw, unprocessed headers from the client request. HTTP_ACCEPT specifically is the requested response format. request.format is the parsed response type Rails understood from the request headers. Somehow, it has converted the JSON based request type to text/html. This explains why our app is complaining that no template exists for an HTML response. Rails thinks the request wants HTML formatted data.

A fix

class ApplicationController < ActionController::Base  
    # ...

    before_action :coerce_json

    def coerce_json
        # Rails converts the following header:
        #   Accept: application/json, text/javascript, */*; q=0.01
        # into text/html. Force it back to json.
        if request.headers[ 'HTTP_ACCEPT' ] =~ /^\s*application\/json/
            request.format = 'json'

This code is relatively self-explanatory. By adding a before_action to the application_controller, our app will reassign the response format before the actual controller code executes.

Some will wonder why I used a regex instead of a hard string comparison. The reason is that i won't pretend i know how different libraries use the Accept header. But i do know that if my app requests application/json as a return type, my Rails app better jolly well return JSON.

In a later post, i will go into the details of how the Rails code does this.

tl;dr: Rails overwrites the jQuery-style JSON request type to "text/html". This function will make Rails understand it correctly.

Building great products is hard. Let's get better at it.
Author image
Written by Ben
Denver, CO
VP of Engineering at MeetMindful. Have feedback or questions? Want to chat? Send me an email.