diff --git a/CHANGELOG.md b/CHANGELOG.md index 04d667e8de..45f747a27a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Next Release ============ -* Your contribution here. +* [#352](https://github.com/intridea/grape/pull/352): Modified registration/execution order of Rack Middleware. This fixes issues with Rack::JSONP responses that contain a `Grape::Entity` - [@deckchair](https://github.com/deckchair). 0.3.2 (2/28/2013) ================= diff --git a/Gemfile b/Gemfile index 4c1106ff44..1ba7d611cd 100644 --- a/Gemfile +++ b/Gemfile @@ -14,4 +14,5 @@ group :development, :test do gem 'rack-test', "~> 0.6.2", :require => "rack/test" gem 'github-markup' gem 'cookiejar' + gem 'rack-contrib' end diff --git a/README.md b/README.md index e18aebf76c..971917f46f 100644 --- a/README.md +++ b/README.md @@ -796,6 +796,43 @@ The order for choosing the format is the following. * Use the default format, if specified by the `default_format` option. * Default to `:txt`. +### JSONP & CORS + +Grape does not directly support JSONP or CORS so support is enabled through the use of available Rack middleware +libraries. + +Add the relevant libraries to your Gemfile e.g. + +* [JSONP (as part of rack-contrib)](https://github.com/rack/rack-contrib) +* [CORS](https://github.com/cyu/rack-cors) + +```ruby +require 'rack/contrib' +require 'rack/cors' + +class API < Grape::API + + # Enable JSONP + # https://github.com/rack/rack-contrib + use Rack::JSONP + + # Enable CORS + # https://github.com/cyu/rack-cors + use Rack::Cors do + allow do + origins '*' + resource '*', :headers => :any, :methods => :get + end + end + + format :json + + get '/' do + 'Hello World' + end +end +``` + ## Content-type Content-type is set by the formatter. You can override the content-type of the response at runtime diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 78eaf17346..1a18b0bd20 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -389,6 +389,16 @@ def run(env) def build_middleware b = Rack::Builder.new + aggregate_setting(:middleware).each do |m| + m = m.dup + block = m.pop if m.last.is_a?(Proc) + if block + b.use *m, &block + else + b.use *m + end + end + b.use Rack::Head b.use Grape::Middleware::Error, :default_status => settings[:default_error_status] || 403, @@ -417,16 +427,6 @@ def build_middleware :formatters => settings[:formatters], :parsers => settings[:parsers] - aggregate_setting(:middleware).each do |m| - m = m.dup - block = m.pop if m.last.is_a?(Proc) - if block - b.use *m, &block - else - b.use *m - end - end - b end diff --git a/spec/grape/entity_spec.rb b/spec/grape/entity_spec.rb index 2d56562c07..e0cf10807e 100644 --- a/spec/grape/entity_spec.rb +++ b/spec/grape/entity_spec.rb @@ -197,14 +197,18 @@ def initialize(args) last_response.body.should == '{"example":{"name":"johnnyiller"}}' end - it 'presents with jsonp and a custom formatter' do + it 'presents with jsonp utilising Rack::JSONP' do + require 'rack/contrib' + + # Include JSONP middleware + subject.use Rack::JSONP + entity = Class.new(Grape::Entity) entity.root "examples", "example" entity.expose :name - subject.content_type :jsonp, 'application/javascript' - subject.formatter :jsonp, lambda { |object, env| object.to_json } - subject.format :jsonp + # Rack::JSONP expects a standard JSON response + subject.format :json subject.get '/example' do c = Class.new do @@ -216,10 +220,11 @@ def initialize(args) present c.new({:name => "johnnyiller"}), :with => entity end - get '/example' + + get '/example?callback=abcDef' last_response.status.should == 200 last_response.headers['Content-type'].should == "application/javascript" - last_response.body.should == '{"example":{"name":"johnnyiller"}}' + last_response.body.should == 'abcDef({"example":{"name":"johnnyiller"}})' end end