So I ran acrross a bug: 3.0.20: UTF8-encoded JSON request params result in Encoding::CompatibilityError (https://github.com/rails/rails/issues/9136( in Rails 3.0.20. Which is no longer supported. And ended up writing the Rack middlewear listed below as a ‘fix’.
My code is heavily inspired by this post here by Pivotal Labs: Sanitizing POST params with custom Rack middleware (http://pivotallabs.com/sanitizing-post-params-with-custom-rack-middleware/)
Rack Middlewear for force_encoding request bodies to UTF-8
JSONScrubber # config/initializers/json_scrubber.rb require 'uri' class JSONScrubber def initialize(app, options) @app = app @routes = options[:routes] end def call(env) scrub(env) @app.call(env) end private def scrub(env) return unless @routes.include?(env['PATH_INFO']) rack_input = env['rack.input'].read.force_encoding(Encoding::UTF_8) env['rack.input'] = StringIO.new(rack_input) ensure env['rack.input'].rewind end end Rails.application.config.middleware.insert_before(0, JSONScrubber, routes: ['/api/some_action_here.json']) JSONScrubberSpec # json_scrubber_spec.rb # encoding: UTF-8 require 'spec_helper' describe JSONScrubber do let(:app) { ->(env) { [200, env, "app"] } } let :middleware do JSONScrubber.new(app, routes: ['/api/some_action_here.json']) end it 'converts non UTF-8 (ASCII-8BIT) encoded response bodies (with special characters) to UTF-8 encoded response bodies' do env = env_for('http://app.abcapp.com.au/api/some_action_here.json') request_array = set_input_and_call env, 'ASCII-8BIT' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('UTF-8') end it 'process UTF-8 encoded response bodies (with special characters) without issue' do env = env_for('http://app.abcapp.com.au/api/some_action_here.json') request_array = set_input_and_call env, 'UTF-8' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('UTF-8') end it "will only modify requests who's paths match the given path options" do env = env_for('http://app.abcapp.com.au/api/dont_touch_me.json') request_array = set_input_and_call env, 'ASCII-8BIT' expect(request_array[0]).to eq(200) expect(request_array[1]['rack.input'].read.encoding.to_s).to eq('ASCII-8BIT') end def env_for url, opts={} Rack::MockRequest.env_for(url, opts) end def set_input_and_call env, encoding rack_input = %q("{"data":{"name":"Name ❤ ☀ ☆ ☂ ☻ ♞ ☯ ☭ ☢ € → ☎ ❄ ♫ ✂ ▷ ✇ ♎ ⇧ ☮ ♻ ⌘ ⌛ ☘","email":"[email protected]","org":"Sch☂ ☻ool","day":"Teacher"}}") env['rack.input'] = StringIO.new(rack_input.force_encoding(encoding)) env['rack.input'].rewind middleware.call(env) end end This should force_encode all request bodies to UTF-8. So if they happen to have any special unicode characters it won’t break your code.
...