Rails在CORS预检选项请求中响应404

时间:2013-12-28 20:47:27

标签: ruby-on-rails-4 xmlhttprequest cors rails-routing http-method

我正在使用Rails 4创建一组服务,我正在使用JavaScript浏览器应用程序。跨源GETS工作正常,但我的POST未通过预检OPTIONS检查404错误。至少,我认为这是正在发生的事情。以下是控制台中出现的错误。这是Mac上的Chrome 31.0.1650.63。

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

我搜索了关于启用CORS的指令的高低,我很难过。通常的建议似乎是将这样的东西放在Application控制器中,我做了。

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

在routes.rb中的某种路由后面会在OPTIONS请求进入时重定向到此操作。

match "/*all" => "application#cors_preflight_check", :constraints => { :method => "OPTIONS" }

'match'指令在Rails 4中不再有效,所以我摆弄它,试图让它直接匹配POSTS,如下所示:

post "/*all" => "application#cors_preflight_check", :constraints => { :method => :options }

但它仍然无效。由于GET请求正在运行,我假设我缺少的是OPTIONS请求的正确路由。但是,我已经尝试了我能想到的每一条路线,似乎没有任何东西可以让请求通过。

我也尝试安装cyu/rack-cors,这会产生相同的结果。

任何人都知道我做错了什么?

8 个答案:

答案 0 :(得分:24)

这是 rack-cors gem 的解决方案,您说过您尝试过。正如其他人所提到的那样,您没有详细说明您使用的前端框架以及实际请求的外观。所以以下内容可能不适用于您,但我希望它对某人有所帮助。

在我的情况下,宝石工作正常,直到我使用PUT(或PATCH或DELETE)。

如果您查看浏览器开发人员控制台,请查看请求标头,您应该有这样一行:

Access-Control-Request-Method: PUT

需要注意的重要一点是,您传递给methods的{​​{1}}适用于resource,而不是飞行前检查后的请求方法。

请注意我Access-Control-Request-Method如何包含我关注的所有方法。

因此,对于:methods => [:get, :post, :options, :delete, :put, :patch]

,整个配置部分看起来应该是这样的
development.rb

答案 1 :(得分:11)

使用Rails 3.2.11。

我把

match '*path', :controller => 'application', :action => 'handle_options_request', :constraints => {:method => 'OPTIONS'}

在我的routes.rb文件中。关键是把它作为最高优先级(在routes.rb文件之上)。创建了该操作,以便公开可用:

  def handle_options_request
    head(:ok) if request.request_method == "OPTIONS"
  end

应用程序控制器中的过滤器:

 after_filter :set_access_control_headers

  def set_access_control_headers
    headers['Access-Control-Allow-Origin'] = '*'
    headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
  end

答案 2 :(得分:6)

是的,正如其他人所指出的那样,创业板可能会做得更好。但由于我非常喜欢使用cors代码在original blog post中指出的方法,所以如果您使用该代码,我会找到 Rails 4 解决方案。

在您的routes.rb中:

match '*all' => 'my_method_name#cor', via: :options

在my_method_name控制器中:

def cor
    # blank section for CORR
    render :text => ''
end

只要您拥有其他代码:

before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
...

然后你应该为Rails 4设置。

答案 3 :(得分:3)

也许这个要点可以帮到你:CORS in Rails 4 APIs

它将OPTIONS方法添加到路由定义中,并向API基本控制器添加一个过滤器,该过滤器使用正确的标头直接响应OPTIONS个请求,并为所有其他标头设置正确的CORS标头行动也是。

答案 4 :(得分:2)

使用Rails 4和Devise来解决这个问题。我最终使用了Calvin Yu的Rack CORS middleware gem。一个很棒的免费博客article

答案 5 :(得分:1)

我不确定你正在使用的是什么javascript前端框架(或者你是否),因为你没有详细说明你在客户端做什么来连接你的Rails 4 API,但我想我如果对任何人有帮助,请添加我的答案。

当使用AngularJS前端的Devise gem连接到Rails 4 API时,我遇到了完全相同的问题(两者都在不同的localhost端口上运行)。我试图使用来自AngularJS表单的POST请求登录到后端,但我一直收到404 NOT FOUND错误,因为我发送了带有预检的OPTIONS请求。花了两天多的时间才弄清楚如何解决这个问题。

基本上,您需要为前端设置代理服务器(Angular,Backbone等),以便连接到您的API,以便您的前端认为请求使用相同的来源。有some straightforward solutions用于使用GruntJS设置代理。我正在使用Gulp为我的项目使用Gulp-Connect和代理中间件进行以下设置(基于找到的解决方案here):

var gulp            = require('gulp'),
    connect         = require('gulp-connect');

gulp.task('devServer', function() {
      connect.server({
        root: './dist',
        fallback: './dist/index.html',
        port: 5000,
        livereload: true,
        middleware: function(connect, o) {
            return [ (function() {
                var url = require('url');
                var proxy = require('proxy-middleware');
                var options = url.parse('http://localhost:3000/');
                options.route = '/api';
                return proxy(options);
            })() ];
        }
      });
    });

我希望这有助于某人!

答案 6 :(得分:1)

我遇到了同样的问题,目前正在针对任何可能的安全/性能问题评估以下路由。他们解决了这个问题,但是......

match '/', via: [:options], 
 to:  lambda {|env| [200, {'Content-Type' => 'text/plain'}, ["OK\n"]]}
match '*unmatched', via: [:options],  
 to:  lambda {|env| [200, {'Content-Type' => 'text/plain'}, ["OK\n"]]}

尽管'匹配'据说不能在Rails 4中工作,显然,如果你将它限制在一个特定的方法, 可以工作。

答案 7 :(得分:0)

这是带有 gem rack-cors 的 Rails 5 解决方案。

0) 我们想要做什么?

这是关于 CORS(跨源资源共享)的 super clear article

您的应用需要接受预检请求,这些请求是 OPTIONS 使用如下特殊标头发出的请求。在这种情况下,浏览器正在检查来自 https://some-site.com 的 POST 请求的授权,给定的 Request-Headers 可以是任何东西。

Origin: https://some-site.com 
Access-Control-Request-Method: POST 
Access-Control-Request-Headers: x-csrf-ump-token

并使用带有适当标题的简单文本内容(无关紧要)进行响应,如下所示:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Headers: x-csrf-ump-token
Access-Control-Max-Age: 1728000

注意:Access-Control-Allow-Headers 是可选的。我们将在下面将其设置为 any

1) 处理 OPTIONS 请求

# config/routes.rb
match '*all', to: 'cors#preflight_check', via: [:options]

# app/controllers/cors_controller.rb
class CorsController < ApplicationController
  def preflight_check
    render plain: 'OK!'
  end
end

2) 现在用 rack-cors 添加适当的标题

# config/initializers/cors.rb

# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
# Read more: https://github.com/cyu/rack-cors

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head],
      max_age: 1728000
  end
end

选中 this article 以针对各种来源进一步自定义此配置。