我在一个项目中前后端彻底分离,前端由 AngularJS 的$http
发起 Ajax 请求 API 数据,请求如下:
$http
method: 'GET'
url: "#{window.ApiBaseUrl}books.json"
isArray: true
.success (data) ->
$s.books = data
而该 API 由 Rails 提供,显而易见,会发生如下错误
XMLHttpRequest cannot load http://localhost:3000/api/books.json. Origin http://localhost:8080 is not allowed by Access-Control-Allow-Origin.
如果希望允许跨域,显然是需要在 API 返回值的 Header 中加入数据,如我猜想的做法是这样的:
class Api::BaseController < ActionController::Base
protect_from_forgery with: :null_session
respond_to :json
before_action :allow_cross_domain_access
before_action :cors_preflight_check
after_action :cors_set_access_control_headers
def allow_cross_domain_access
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
headers['Access-Control-Allow-Headers'] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(',')
headers['Access-Control-Max-Age'] = '1728000'
end
def cors_set_access_control_headers
headers['Access-Control-Allow-Origin'] = '*'
headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(',')
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, GET, OPTIONS'
headers['Access-Control-Allow-Headers'] = 'X-Requested-With, X-Prototype-Version'
headers['Access-Control-Max-Age'] = '1728000'
render :text => '', :content_type => 'text/plain'
end
end
end
对于:cors_preflight_check
方法,是用来处理发起 Ajax 请求前先行的OPTIONS
请求的,但如上操作没有正确运行,因为 Rails 的 Routs 爆出错误:
Started OPTIONS "/api/books.json" for ::1 at 2015-02-26 23:29:29 +0800
ActionController::RoutingError (No route matches [OPTIONS] "/api/books.json"):
... ...
于是我考虑可能是我的思路有问题,因为 rack 是先找到 http method 与 url 对应的方法之后才会由 controller 执行 before_action 的,所以一般遇到这样的情况,都会如何解决呢?或有什么案例可供借鉴一下的么?