-
Notifications
You must be signed in to change notification settings - Fork 0
24b Cross Origin Requests
If we want to allow 3rd party clients to use the JSON provided via JavaScript and Ajax, we need to tell the web browsers that request through the API, that it is okay to use.
Without this client developers will get errors like XMLHttpRequest cannot load http://yoursite.com/notes.json. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
We need to enable [CORS - Cross Origin Request Sharing], by setting a few headers.
This is a pretty good article explaining why.
Since we only need this for the API, and we want it to cover the entire API, let's add a before_action
to API::APIController
.
app/controllers/api/api_controller.rb
class API::APIController < ApplicationController
# ...
before_action :set_access_control_headers
# ...
private
def set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, PUT, DELETE, GET, OPTIONS'
headers['Access-Control-Request-Method'] = '*'
headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, Authorization'
end
# ...
Now this will set the correct headers, however, some JavaScript clients will perform a "preflight" of the request with the OPTIONS
HTTP method (which is usually not used in a normal Rails project).
Rails' RESTful route declarations (like resources :users
), won't create route entries for OPTIONS method requests, so we need to either add them to each resource that can be accessed or, since this is an API, write a blanket route to cover everything under /api/
. Add this line to the routes.rb
file:
config/routes.rb
match '/api/*path' => 'api/api#preflight', via: [:options]
And add a public method to api_controller.rb
to serve a 200 response, no matter what. Our app doesn't really need or support OPTIONS, but we need to conform to the spec. Since we don't actually need the body of the response, just a message saying "we got your message ok".
app/controllers/api/api_controller.rb
def preflight
head :ok
end
Now, you should be able to access the API over Ajax with something like:
$.get('http://yoursite.com/api/v1/notes.json?api_key=' + my_key +, function(data) {
console.log(data);
});
This kind of seems a bit hackish, but now I think we understand the issue a bit more clearly.
In true Rails fashion, let's replace it with a gem.
In API::APIController
, remove the before_action
for :set_access_control_headers
, as well as the method itself, and also remove the preflight
action/method.
In routes.rb
, remove the match '/api/*path'
catchall route for OPTIONS requests.
In your Gemfile
, add the Rack::Cors
rack middleware and bundle:
Gemfile
gem 'rack-cors', require: 'rack/cors'
$ bundle install
Then, add a configuration that covers everything under /api/
to config/application.rb
, inside the class Application
declaration:
config/application.rb
# Rack::Cors configuration
config.middleware.insert_before 0, 'Rack::Cors', debug: true, logger: Rails.logger do
allow do
origins '*'
resource '/api/*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options], max_age: 0
end
end
This will intercept those requests and set the correct headers for us.
Commit.