Rails和CORS:为什么我只看到OPTIONS请求?

时间:2015-08-30 16:57:32

标签: javascript ruby-on-rails json cors

我正在努力让CORS在我的JSON api Rails应用程序中正常工作。

我有一个Event模型和相关的控制器。 Event属于RegisteredApplication s。

# Routes.rb
Rails.application.routes.draw do

  root 'registered_applications#index'

  resources :registered_applications

  namespace :api, defaults: { format: :json } do
    # OPTION is an HTTP verb
    match '/events', to: 'events#create', via: [:options]
    resources :events, only: [:create]
  end

  devise_for :users

end
# events_controller.rb
class API::EventsController < ApplicationController

  skip_before_filter :verify_authenticity_token
  before_filter :cors_preflight_check
  after_filter :cors_set_access_control_headers

  # If this is a preflight OPTIONS request, then short-circuit the request,
  # return only the necessary headers and return an empty text/plain.
  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

  # For all responses in this controller, return the CORS access control headers.
  def cors_set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
    headers['Access-Control-Max-Age'] = "1728000"
  end

  def create
    registered_application = RegisteredApplication.find_by(url: request.env['HTTP_ORIGIN'])

    if registered_application.nil?
      render json: "Unregistered application", status: :unprocessable_entity
    else
      event = registered_application.events.new(event_params)

      if event.save
        render json: @event, status: :created
      else
        render @event.errors, status: :unprocessable_entity
      end
    end
  end

private

  def event_params
    params.permit(:name)
  end

end

我用来将新点击事件发送到我的Rails应用程序的JavaScript:

$( document ).ready(function() {
  $( window ).click( function() {
      metrics.report("click");
  });
});

var metrics = {};

metrics.report = function(eventName) {
  var event = { name: eventName };
  var request = new XMLHttpRequest();

  request.open("POST", "http://localhost:3000/api/events", true);
  request.setRequestHeader('Content-Type', 'application/json');

  request.send(JSON.stringify(event));
}

有两个问题:

1)当我通过上述javascript(来自其他网页)发出请求时,我收到XMLHttpRequest cannot load http://localhost:3000/api/events. Request header field Content-Type is not allowed by Access-Control-Allow-Headers错误。我的Rails api要求请求是JSON,所以我不知道我应该怎么做。如果我注释掉setRequestHeader()行,则错误消失,但我的Rails应用程序未收到初始HTTP OPTIONS请求。

2)在Rails应用程序端,我看到OPTIONS请求,但第二个(POST)请求永远不会发生。这是我日志中的唯一内容:

Started OPTIONS "/api/events" for ::1 at 2015-08-30 12:54:17 -0400
Processing by API::EventsController#create as JSON
  RegisteredApplication Load (0.2ms)  SELECT  "registered_applications".* FROM "registered_applications" WHERE "registered_applications"."url" = $1  ORDER BY created_at DESC LIMIT 1  [["url", "http://localhost:2222"]]
Unpermitted parameter: format
   (0.1ms)  BEGIN
  SQL (0.4ms)  INSERT INTO "events" ("registered_application_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["registered_application_id", 21], ["created_at", "2015-08-30 16:54:17.751257"], ["updated_at", "2015-08-30 16:54:17.751257"]] (0.4ms)  COMMIT
Completed 201 Created in 11ms (Views: 0.1ms | ActiveRecord: 1.1ms)

我错过了一些明显的东西吗?

1 个答案:

答案 0 :(得分:2)

我认为您需要在服务器端代码的headers['Access-Control-Allow-Headers'] = "Content-Type"函数中添加cors_set_access_control_headers

  

My Rails api要求请求是JSON,所以我不知道我应该怎么做。如果我注释掉setRequestHeader()行,那么错误就会消失,但是我的Rails应用程序没有收到初始的HTTP OPTIONS请求。

根据CORS规范,如果您发送的Content-Type请求标头的值不是application/x-www-form-urlencodedmultipart/form-datatext/plain,那么CORS预检{ {1}}请求已发送。

因此,当您发送带有OPTIONS值的Content-Type标头时,它会触发application/json请求。但是当您删除OPTIONS行时,则不会发送自定义setRequestHeader()标头(并且没有其他自定义“作者”标头),因此在这种情况下,不需要CORS预检,因此不会{{1请求已经完成。