I'm creating a set of services using Rails 4, which I am consuming with a JavaScript browser application. Cross-origin GETS are working fine, but my POSTs are failing the preflight OPTIONS check with a 404 error. At least, I think that's what's happening. Here are the errors as they appear in the console. This is Chrome 31.0.1650.63 on a Mac.
OPTIONS http://localhost:3000/confessor_requests 404 (Not Found) jquery-1.10.2.js:8706
OPTIONS http://localhost:3000/confessor_requests No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. jquery-1.10.2.js:8706
XMLHttpRequest cannot load http://localhost:3000/confessor_requests. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. main.html:1
I've searched high and low for instructions on enabling CORS, and I'm stumped. The usual recommendation seems to be to put something like this in the Application controller, which I did.
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = "1728000"
end
def cors_preflight_check
if request.method == :options
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = '*'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
Followed by some kind of route in routes.rb that will redirect to this action when an OPTIONS request comes in.
match "/*all" => "application#cors_preflight_check", :constraints => { :method => "OPTIONS" }
The 'match' directive no longer works in Rails 4, so I fiddled with it, attempting to make it match POSTS directly, like this:
post "/*all" => "application#cors_preflight_check", :constraints => { :method => :options }
But it still doesn't work. Since the GET requests are working, I'm assuming that what I'm missing is the correct route for the OPTIONS request. However, I've tried every route I can think of, and nothing seems to let the request through.
I also tried installing cyu/rack-cors, and this gives the same result.
Anyone know what I'm doing wrong?
Here's a solution with the rack-cors gem, which you said you tried. As others have mentioned, you didn't give much detail in regards to which front-end framework you're using and what the actual request looks like. So the following may not apply to you, but I hope it helps someone.
In my case, the gem worked fine until I used PUT (or PATCH or DELETE).
If you look in your browser developer console, look at the request headers, and you should have a line like this:
The important thing to note is that the
methods
you pass toresource
are for theAccess-Control-Request-Method
, not the Request Method that is to come after the pre-flight check.Note how I have
:methods => [:get, :post, :options, :delete, :put, :patch]
that will include all the methods I care about.Thus your entire config section should look something like this, for
development.rb
:Yes, as others have pointed out there is a GEM to maybe do this better. But since I very much liked the method pointed out in the original blog post with the cors code, I've found the Rails 4 solution if you're using that code.
In your routes.rb:
In your my_method_name controller:
As long as you have that plus your other code:
Then you should be set for Rails 4.
I'm not sure what javascript front end framework you are using (or if you are) since you didn't elaborate on what you are doing on the client-side to connect to your Rails 4 API, but I thought I'd add my answer in case it helps anybody.
I ran into the exact same problem while connecting to a Rails 4 API with the Devise gem from an AngularJS front end (both were running on separate localhost ports). I was trying to log in to the back end using a POST request from an AngularJS form but I kept getting a 404 NOT FOUND error because I was sending an OPTIONS request with preflight. It took more than 2 days to figure out how to fix the problem.
Basically you need to set up a proxy server for your front end (Angular, Backbone, whatever) in order to connect to your API so that your front end thinks that the request is using the same origin. There are some straightforward solutions out there for setting up Proxies using GruntJS. I am using Gulp for my project with Gulp-Connect and proxy-middleware with the following setup (based on the solution found here):
I hope this helps someone!
Perhaps this gist can help you: CORS in Rails 4 APIs
It adds the
OPTIONS
method to the route definition, and adds a filter to the API base controller which directly responds toOPTIONS
requests with the correct header, and sets the correct CORS headers for all other actions, too.Working on Rails 3.2.11.
I put
in my routes.rb file. The key was to put it as top priority (on top of the routes.rb file). Created that action so that it is publicly available:
And a filter in application controller:
I ran into the same issue, and am currently evaluating the following routes for any possible security / performance issues. They solve the issue, but...
Despite 'match' supposedly not working in Rails 4, apparently it does work if you restrict it to a specific method.