views:

71

answers:

1

I've got a modular Sinatra app and I'd like to encode the output as JSON when the content-type dictates. At the moment I'm doing so manually in my routes:

get 'someroute' do
    # content-type is actually set with a before filter
    # included only for clarity
    content_type 'application/json', :charset => 'utf-8'
    # .. #
    {:success=>true}.to_json
end

I'd prefer it to look like this:

get 'someroute' do
    content_type 'application/json', :charset => 'utf-8'
    # .. #
    {:success=>true}
end

And I'd like to use Rack middleware to do the encoding if it detects the appropriate content-type.

I've been trying to get the following to work, but to no avail (the content-length gets borked - returns content-length of original content and not JSON encoded content):

require 'init'

module Rack
  class JSON
    def initialize app
      @app = app
    end
    def call env
      @status, @headers, @body = @app.call env
      if json_response?
        @body = @body.to_json
      end
      [@status, @headers, @body]
    end
    def json_response?
      @headers['Content-Type'] == 'application/json'
    end
  end
end

use Rack::JSON

MyApp.set :run, false
MyApp.set :environment, ENV['RACK_ENV'].to_sym

run MyApp

Any pointers to get me back on track?

+1  A: 

You’ve got everything right but one thing: Rack expects the body to be an object that responds to each but is not a string. Just put your body inside an array.

It’s probably not necessary, but if you want to set the content length by hand, just add it to the headers.

if json_response?
  @body = [@body.to_json]
  @headers["Content-Length"] = @body[0].size.to_s
end
Todd Yandell
Thanks, that did the trick! Now I just have to figure out how to blend this with my etag system.
Franky-D