webentwicklung-frage-antwort-db.com.de

Rails Beantwortet CORS Preflight Options Request mit 404

Ich erstelle eine Reihe von Diensten mit Rails 4, die ich mit einer JavaScript-Browseranwendung verwende. Cross-Origin GETS funktionieren einwandfrei, aber meine POSTs bestehen die Preflight-OPTIONEN nicht mit einem 404-Fehler Zumindest denke ich, dass das passiert. Hier sind die Fehler, wie sie in der Konsole angezeigt werden. Dies ist Chrome 31.0.1650.63 auf einem 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

Ich habe hoch und niedrig nach Anweisungen zum Aktivieren von CORS gesucht und bin ratlos. Die übliche Empfehlung scheint zu sein, so etwas in den Application Controller zu schreiben, was ich auch getan habe.

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

Gefolgt von einer Route in routes.rb, die zu dieser Aktion umleitet, wenn eine OPTIONS-Anfrage eingeht.

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

Die 'match'-Direktive funktioniert in Rails 4 nicht mehr, also habe ich damit herumgespielt und versucht, sie direkt mit POSTS abzustimmen, so:

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

Aber es geht immer noch nicht. Da die GET-Anfragen funktionieren, gehe ich davon aus, dass mir die richtige Route für die OPTIONS-Anfrage fehlt. Ich habe jedoch jede erdenkliche Route ausprobiert und nichts scheint die Anfrage durchzulassen.

Ich habe auch versucht, cyu/rack-cors zu installieren, und dies ergibt das gleiche Ergebnis.

Weiß jemand was ich falsch mache?

44
Rolandus

Hier ist eine Lösung mit dem Rack-Cors-Edelstein, von dem Sie sagten, Sie hätten es versucht. Wie andere bereits erwähnt haben, haben Sie nicht genau angegeben, welches Front-End-Framework Sie verwenden und wie die tatsächliche Anforderung aussieht. Daher trifft das Folgende möglicherweise nicht auf Sie zu, aber ich hoffe, es hilft jemandem.

In meinem Fall hat der Edelstein einwandfrei funktioniert, bis ich PUT (oder PATCH oder DELETE) verwendet habe.

Wenn Sie in der Entwicklerkonsole Ihres Browsers nachsehen, sehen Sie sich die Anforderungsheader an, und Sie sollten eine Zeile wie die folgende haben:

Access-Control-Request-Method: PUT

Wichtig ist, dass die methods, die Sie an resource übergeben, für den Access-Control-Request-Method Und nicht für die Anforderungsmethode gelten, die nach der Prüfung vor dem Flug erfolgen soll.

Beachten Sie, wie ich :methods => [:get, :post, :options, :delete, :put, :patch] Habe, das alle Methoden enthält, die mir wichtig sind.

Daher sollte Ihr gesamter Konfigurationsabschnitt ungefähr so ​​aussehen, für 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
24
Tyler Collier

Arbeiten an Rails 3.2.11.

Ich legte

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

in meiner routes.rb Datei. Der Schlüssel war, es als oberste Priorität (oben auf der Datei routes.rb) zu setzen. Diese Aktion wurde so erstellt, dass sie öffentlich verfügbar ist:

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

Und ein Filter im Anwendungscontroller:

 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
11
ancajic

Ja, wie andere darauf hingewiesen haben, gibt es ein Juwel, um dies vielleicht besser zu machen. Aber da mir die im originaler Blog-Beitrag mit dem cors-Code beschriebene Methode sehr gut gefallen hat, habe ich die Rails 4 Lösung, wenn Sie diesen Code verwenden.

In Ihrer routes.rb:

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

In Ihrem Controller my_method_name:

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

Solange du das und deinen anderen Code hast:

before_filter :cors_preflight_check
after_filter :cors_set_access_control_headers
...

Dann sollten Sie eingestellt sein auf Rails 4.

6

Vielleicht kann diese Übersicht Ihnen helfen: CORS in Rails 4 APIs

Es fügt der Routendefinition die Methode OPTIONS hinzu und fügt dem API-Basiscontroller einen Filter hinzu, der direkt auf OPTIONS -Anfragen mit dem richtigen Header antwortet und die richtigen CORS-Header für alle anderen Aktionen festlegt , auch.

3
dhoelzgen

Stieß mit Rails 4 und Devise auf diese Ausgabe. Am Ende verwendete ich Rack CORS middlewaregem von Calvin Yu. Ein großartiger, kostenloser Blog Artikel) .

2
Tom Doe

Ich bin mir nicht sicher, welches Javascript-Front-End-Framework Sie verwenden (oder ob Sie es sind), da Sie nicht genau beschrieben haben, was Sie auf der Clientseite tun, um eine Verbindung zu Ihrem Rails 4) herzustellen API, aber ich dachte, ich würde meine Antwort hinzufügen, falls es jemandem hilft.

Beim Herstellen einer Verbindung zu einer Rails 4 API mit dem Devise Gem von einem AngularJS-Front-End aus (beide liefen auf separaten Localhost-Ports) ist genau das gleiche Problem aufgetreten. Ich habe versucht, mich bei der anzumelden Back-End mit einer POST Anfrage aus einem AngularJS-Formular, aber es wurde immer wieder der Fehler 404 NOT FOUND angezeigt, weil ich eine OPTIONS-Anfrage mit Preflight gesendet habe. Es dauerte mehr als 2 Tage, um herauszufinden, wie das Problem behoben werden konnte das Problem.

Grundsätzlich müssen Sie einen Proxyserver für Ihr Front-End einrichten (Angular, Backbone, was auch immer), um eine Verbindung zu Ihrer API herzustellen, damit Ihr Front-End denkt, dass die Anforderung denselben Ursprung verwendet. Es gibt einige einfache Lösungen für die Einrichtung von Proxies mit GruntJS. Ich verwende Gulp für mein Projekt mit Gulp-Connect und Proxy-Middleware mit folgendem Setup (basierend auf der gefundenen Lösung hier ):

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);
            })() ];
        }
      });
    });

Ich hoffe das hilft jemandem!

1
po3t

Ich bin auf dasselbe Problem gestoßen und prüfe derzeit die folgenden Routen auf mögliche Sicherheits-/Leistungsprobleme. Sie lösen das Problem, aber ...

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"]]}

Obwohl 'match' angeblich in Rails 4 nicht funktioniert, funktioniert es anscheinend funktioniert, wenn Sie es auf eine bestimmte Methode beschränken.

1
RonLugge