Rails 在 CORS 预检选项请求中以 404 响应
Posted
技术标签:
【中文标题】Rails 在 CORS 预检选项请求中以 404 响应【英文标题】:Rails Responds with 404 on CORS Preflight Options Request 【发布时间】:2014-01-16 03:18:27 【问题描述】:我正在使用 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,结果相同。
有人知道我做错了什么吗?
【问题讨论】:
match
应该仍然可以工作,但是如果您没有通过 via
选项,它会抱怨。用post
做这件事绝对行不通。作为之前在rack-cors
上犯过这个错误的人,我还要问:您在配置rack-cors
中间件后是否重新启动了服务器?我花了一个多小时自己追赶那段时间。 :)
这里也一样。我已经尝试了自定义方法和 gem:在引发 ActiveRecord::RecordNotFound
异常之前一切正常。在这种情况下,CORS 不起作用。
我已经进一步调查了这个问题:在我的情况下,预检响应标头是正确的,而实际请求(引发异常时)会产生没有 CORS 标头的响应
【参考方案1】:
这是您说您尝试过的rack-cors gem 解决方案。正如其他人所提到的,您没有详细说明您正在使用哪个前端框架以及实际请求的外观。因此,以下内容可能不适用于您,但我希望它对某人有所帮助。
在我使用 PUT(或 PATCH 或 DELETE)之前,gem 运行良好。
如果您查看浏览器开发者控制台,请查看请求标头,您应该有如下一行:
Access-Control-Request-Method: PUT
需要注意的重要一点是,您传递给resource
的methods
是针对Access-Control-Request-Method
,而不是在飞行前检查之后的请求方法。
请注意我是如何拥有 :methods => [:get, :post, :options, :delete, :put, :patch]
的,其中将包含我关心的所有方法。
因此,对于development.rb
,您的整个配置部分应如下所示:
# This handles cross-origin resource sharing.
# See: https://github.com/cyu/rack-cors
config.middleware.insert_before 0, "Rack::Cors" do
allow do
# In development, we don't care about the origin.
origins '*'
# Reminder: On the following line, the 'methods' refer to the 'Access-
# Control-Request-Method', not the normal Request Method.
resource '*', :headers => :any, :methods => [:get, :post, :options, :delete, :put, :patch], credentials: true
end
end
【讨论】:
谢谢。你拯救了我的一天:) 不是 100% 确定我从哪里复制了我的规则,其他一些 SO 帖子。但在这种情况下,添加put
会使事情再次发生。
同样,用 js fetch()
,method: "PATCH"
不起作用但 method: "PUT"
起作用(都使用了 Access-Control-Request-Method
)
:patch
在 CORS 允许的方法中丢失!谢谢
谢谢!。凭据:真拯救了我的一天!【参考方案2】:
在 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
【讨论】:
这个答案是针对 Rails 3 的,但这个问题专门针对 Rails 4。 @ancajic 感谢您的回答。您知道这有什么安全隐患吗? @Sebastialonso... 显然,如果您的 HTTP API 具有响应使用 method=OPTIONS 的请求的端点,它可能会导致问题。除此之外,我没有看到任何通过启用 CORS 不会发生的安全隐患。更有经验的 Rails 开发人员可能会提供更多帮助;)【参考方案3】:是的,正如其他人指出的那样,有一个 GEM 可能会做得更好。但由于我非常喜欢 original blog post 中使用 cors 代码指出的方法,因此如果您使用该代码,我已经找到了 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 设置好。
【讨论】:
不错的解决方案,我没有在应用程序控制器中手动设置标题,而是根据环境设置它们。对于config/environments/development.rb
中的开发,您可以配置 config.action_dispatch.default_headers =
以包含 COR 标头。【参考方案4】:
也许这个要点可以帮助你:CORS in Rails 4 APIs
它将OPTIONS
方法添加到路由定义中,并向API 基本控制器添加一个过滤器,该过滤器使用正确的标头直接响应OPTIONS
请求,并为所有其他操作设置正确的CORS 标头。
【讨论】:
【参考方案5】:我遇到了同样的问题,目前正在评估以下路线是否存在任何可能的安全/性能问题。他们解决了这个问题,但是......
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 中不起作用,但如果您将其限制为特定方法,显然它确实起作用。
【讨论】:
谢谢,match
和 to: lambda
。一些文档:apidock.com/rails/v6.0.0/ActionDispatch/Routing/Mapper/Base/…【参考方案6】:
Rails 4 和 Devise 遇到了这个问题。我最终使用了 Calvin Yu 的 Rack CORS middleware
gem。一个很棒的免费博客article。
【讨论】:
【参考方案7】:我不确定您正在使用什么 javascript 前端框架(或者如果您正在使用),因为您没有详细说明您在客户端正在做什么以连接到 Rails 4 API,但我想我'会添加我的答案,以防它帮助任何人。
我在使用 AngularJS 前端的 Devise gem 连接到 Rails 4 API 时遇到了完全相同的问题(两者都在不同的 localhost 端口上运行)。我试图使用来自 AngularJS 表单的 POST 请求登录到后端,但我一直收到 404 NOT FOUND 错误,因为我正在发送带有预检的 OPTIONS 请求。花了 2 天多的时间才弄清楚如何解决这个问题。
基本上,您需要为您的前端(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);
)() ];
);
);
我希望这对某人有帮助!
【讨论】:
我不确定实现代理服务器是否真的可以解决这个问题。如果其他人想与您的 API 交互怎么办?你不认为 Rails 应该正确响应选项请求吗? @AdamRobertson - 你可能是对的。如果需要 GET 请求以外的任何内容,我的解决方案不适用于公开使用的 API。此解决方案应仅用于私有 API。也就是说,无论我在 Rails 端做了什么,我都遇到了这个问题。无论我实施了哪种 CORS 解决方案,如果我发出 GET 请求以外的任何内容,我总是会发出 OPTIONS 请求。归结为我缺乏使用 Rails 的经验。【参考方案8】:这是一个带有 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 以进一步自定义此配置,适用于各种来源。
【讨论】:
以上是关于Rails 在 CORS 预检选项请求中以 404 响应的主要内容,如果未能解决你的问题,请参考以下文章
Yii 和 Vue2 CORS 启用。服务器以 404 错误响应预检 OPTIONS 请求