Je crée un ensemble de services en utilisant Rails 4, que je consomme avec une application de navigateur JavaScript. Cross-Origin GETS fonctionne bien, mais mes POST échouent à la vérification OPTIONS de contrôle en amont avec un Erreur 404. Du moins, je pense que c'est ce qui se passe. Voici les erreurs telles qu'elles apparaissent dans la console. C'est Chrome 31.0.1650.63 sur un Mac.
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
J'ai cherché haut et bas pour obtenir des instructions sur l'activation de CORS, et je suis perplexe. La recommandation habituelle semble être de mettre quelque chose comme ça dans le contrôleur d'application, ce que j'ai fait.
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
Suivi d'une sorte de route dans routes.rb qui redirigera vers cette action lorsqu'une demande OPTIONS arrivera.
match "/*all" => "application#cors_preflight_check", :constraints => { :method => "OPTIONS" }
La directive 'match' ne fonctionne plus dans Rails 4, donc je l'ai tripotée, essayant de la faire correspondre directement aux POSTS, comme ceci:
post "/*all" => "application#cors_preflight_check", :constraints => { :method => :options }
Mais ça ne marche toujours pas. Étant donné que les demandes GET fonctionnent, je suppose que ce qui me manque est la bonne route pour la demande OPTIONS. Cependant, j'ai essayé toutes les voies auxquelles je peux penser, et rien ne semble laisser passer la demande.
J'ai également essayé d'installer cyu/rack-cors , et cela donne le même résultat.
Quelqu'un sait ce que je fais mal?
Voici une solution avec le gemme rack-cors, que vous avez dit avoir essayé. Comme d'autres l'ont mentionné, vous n'avez pas donné beaucoup de détails sur le cadre frontal que vous utilisez et à quoi ressemble la demande réelle. Donc, ce qui suit peut ne pas s'appliquer à vous, mais j'espère que cela aide quelqu'un.
Dans mon cas, la gemme a bien fonctionné jusqu'à ce que j'utilise PUT (ou PATCH ou DELETE).
Si vous regardez dans la console de développement de votre navigateur, regardez les en-têtes de demande, et vous devriez avoir une ligne comme celle-ci:
Access-Control-Request-Method: PUT
La chose importante à noter est que les methods
que vous passez à resource
sont pour les Access-Control-Request-Method
, pas la méthode de demande qui doit suivre après le contrôle pré-vol.
Notez comment j'ai :methods => [:get, :post, :options, :delete, :put, :patch]
qui inclura toutes les méthodes qui me tiennent à cœur.
Ainsi, toute votre section de configuration devrait ressembler à ceci, pour 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
Travailler sur Rails 3.2.11.
Je mets
match '*path', :controller => 'application', :action => 'handle_options_request', :constraints => {:method => 'OPTIONS'}
dans mon fichier routes.rb. La clé était de le mettre en priorité absolue (en haut du fichier routes.rb). A créé cette action pour qu'elle soit publiquement disponible:
def handle_options_request
head(:ok) if request.request_method == "OPTIONS"
end
Et un filtre dans le contrôleur d'application:
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
Oui, comme d'autres l'ont souligné, il existe un GEM pour peut-être faire mieux. Mais comme j'ai beaucoup aimé la méthode indiquée dans le article de blog original avec le code cors, j'ai trouvé les Rails 4 solution si vous utilisez ce code.
Dans vos routes.rb:
match '*all' => 'my_method_name#cor', via: :options
Dans votre contrôleur my_method_name:
def cor
# blank section for CORR
render :text => ''
end
Tant que vous en avez, plus votre autre code:
before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
...
Ensuite, vous devez être défini pour Rails 4.
Peut-être que ce Gist peut vous aider: CORS dans Rails 4 API
Il ajoute la méthode OPTIONS
à la définition de l'itinéraire et ajoute un filtre au contrôleur de base API qui répond directement aux requêtes OPTIONS
avec l'en-tête correct et définit les en-têtes CORS corrects pour toutes les autres actions , aussi.
Je ne sais pas quel framework frontal javascript vous utilisez (ou si vous l'êtes) car vous n'avez pas précisé ce que vous faites du côté client pour vous connecter à votre Rails 4 API, mais j'ai pensé ajouter ma réponse au cas où cela aiderait quelqu'un.
J'ai rencontré exactement le même problème lors de la connexion à une API Rails 4 avec la gemme Devise à partir d'un frontal AngularJS (les deux fonctionnaient sur des ports locaux différents). J'essayais de me connecter à la back-end en utilisant une demande POST à partir d'un formulaire AngularJS mais j'ai continué à recevoir une erreur 404 NOT FOUND parce que j'envoyais une demande OPTIONS avec contrôle en amont. Il a fallu plus de 2 jours pour comprendre comment résoudre le problème le problème.
Fondamentalement, vous devez configurer un serveur proxy pour votre frontal (Angular, Backbone, peu importe) afin de vous connecter à votre API afin que votre frontal pense que la demande utilise la même origine. Il existe quelques solutions simples pour la configuration de proxy à l'aide de GruntJS. J'utilise Gulp pour mon projet avec Gulp-Connect et proxy-middleware avec la configuration suivante (basée sur la solution trouvée ici ):
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);
})() ];
}
});
});
J'espère que ça aidera quelqu'un!
J'ai rencontré le même problème et j'évalue actuellement les itinéraires suivants pour tous les problèmes de sécurité/performances possibles. Ils résolvent le problème, mais ...
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"]]}
Bien que 'match' ne fonctionne censément pas dans Rails 4, apparemment cela fonctionne fonctionne si vous le limitez à une méthode spécifique.