Ascent

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'
		end
	end
end

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.

Get the latest posts delivered right to your inbox.
Author image
Written by Ben
Ben is the co-founder of Skyward. He has spent the last 10 years building products and working with startups.