Ben Aubin
Ben Aubin

Reputation: 5667

Allow CORS in Ruby on Rails

In my config/application.rb file, I have this code,

config.action_dispatch.default_headers = {
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Request-Method' => 'GET, PATCH, PUT, POST, OPTIONS, DELETE'
    }

But that does not allow me to send a post request to a route on my sever

Safari gives this error:

http://localhost:3000/studentsFailed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:3000/studentsFailed to load resource: Origin http://localhost:4200 is not allowed by Access-Control-Allow-Origin.
localhost:1XMLHttpRequest cannot load http://localhost:3000/students. Origin http://localhost:4200 is not allowed by Access-Control-Allow-Origi

And in my Rails server console:

Started OPTIONS "/students" for ::1 at 2015-03-28 21:00:45 -0500

ActionController::RoutingError (No route matches [OPTIONS] "/students"):

Upvotes: 15

Views: 27493

Answers (5)

errata
errata

Reputation: 26972

I spent some time working on this and I can tell you the most reliable solution is to use rack-cors. see: https://github.com/cyu/rack-cors

First add the gem:

gem 'rack-cors', '~> 0.3.1'

then in application.rb add

config.middleware.insert_before ActionDispatch::Static, Rack::Cors do
  allow do
    origins '*'
    resource '*', :headers => :any, :methods => [:get, :post, :options]
  end
end

If your production app does not serve static assets (such as if you use a serve like nginx or apache), consider replacing ActionDispatch::Static in the above example with 0. See https://github.com/cyu/rack-cors#common-gotchas for more information about the argument.

Upvotes: 23

user398520
user398520

Reputation: 1663

Rails 5

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*', headers: :any, methods: [:get, :post, :options]
  end
end

Upvotes: 1

Ben Aubin
Ben Aubin

Reputation: 5667

I was able to figure this out with a bit of help from @Akiomi's answer:

In my routes.rb, I added the following code to the top of the file:

  match '(:anything)' => 'application#nothing', via: [:options]

Next, in my application controller, I added:

def nothing
    render text: '', content_type: 'text/plain'
end

Along with the headers in config/application.rb:

config.action_dispatch.default_headers = {
    'Access-Control-Allow-Origin' => '*',
    'Access-Control-Request-Method' => 'GET, PATCH, PUT, POST, OPTIONS, DELETE',
    'Access-Control-Allow-Headers:' => 'Origin, X-Requested-With, Content-Type, Accept'
}

Yes, notice the 'Access-Control-Allow-Headers:' => 'Origin, X-Requested-With, Content-Type, Accept' that was not included in my original question, this is one of the big problems.

Upvotes: 7

Akiomi KAMAKURA
Akiomi KAMAKURA

Reputation: 71

Add the following code:

In config/routes.rb:

match 'students' => 'students#option', via: [:options]

In controllers/student_controller.rb:

def option
  render text: '', content_type: 'text/plain'
end

Or you can use rack-cors.

Upvotes: 2

Frederick Cheung
Frederick Cheung

Reputation: 84182

In some cases a browser will do a preflight request: rather than actually doing the request it first does an OPTIONS request to the same url, so that it can find out what the values of the various CORS headers are (More on preflighting here). If this request is successful and the headers have the correct values, it then does the actual request.

You haven't added a route for these options requests, so they're going through to the rails 404 page which doesn't include the CORS headers.

The OPTIONS response just needs to set the same CORS headers as you would normally set during a request. It shouldn't do anything else. For example

match 'students' => 'students#cors_preflight', via: [:options]

def cors_preflight
  render nothing: true
end

Note that there are other CORS headers you may need to set such as Access-Control-Allow-Credentials, Access-Control-Allow-Headers

When you've got this working you may wish to consider tightening this up a little - you are potentially opening your app to cross site scripting attacks.

Upvotes: 1

Related Questions