2012-04-12 3 views
16

Mit Gerät verwendet man before_filter :authenticate_user!, um den Zugriff nur auf authentifizierte Benutzer zu beschränken.Wie kann ich den 401-Status des Geräts in AJAX elegant handhaben?

Wenn ein unauthenticated Benutzer versucht, sowieso eine eingeschränkte Seite zu besuchen, ersinnen führt automatisch eine Umleitung auf die Zeichen in Seite.

Wenn Sie versuchen, http://localhost:3000/users/edit zu öffnen, führt dies zu einer Weiterleitung an http://localhost:3000/users/sign_in.

Nun, wenn ich den Link http://localhost:3000/users/edit als :remote => true definieren, entwickeln nur einen Statuscode 401 über JS erteilen.

Wie kann ich elegant mit dieser Situation fertig zu werden und sie in eine Overlay OR Umleitung als nicht-Remote-Variante täte den Anmeldedialog angezeigt werden?

Bietet devise eine Standardstrategie für diese Situation, die ich einfach aktivieren müsste?

Antwort

12

Dies ist die Lösung, die ich für jetzt wählte (in Coffeescript-Syntax):

$ -> 
    $("a").bind "ajax:error", (event, jqXHR, ajaxSettings, thrownError) -> 
    if jqXHR.status == 401 # thrownError is 'Unauthorized' 
     window.location.replace('/users/sign_in') 

jedoch diese (auf seine eigene ist) über die Seite vergisst nur der Benutzer zunächst besuchen wollte, die Nutzbarkeit beschränkt.

Für eine elegantere Handhabung ist eine zusätzliche (Controller-) Logik erforderlich.

UPDATE: korrekte Umleitung

Innerhalb der Funktion hält this die ursprüngliche URL der Benutzer gehen soll.

Durch den Aufruf window.location.replace(this) (statt explizit auf das Zeichen in Seite umleiten), wird die App versuchen, den Benutzer an das ursprünglich vorgesehene Ziel umzuleiten.

Obwohl immer noch unmöglich (nicht autorisiert), wird dies jetzt ein GET-Aufruf sein (anstelle von JS/AJAX). Daher ist Devise in der Lage zu treten und den Benutzer auf die Anmeldeseite umzuleiten.

Von da an arbeitet Devise wie üblich, in den Benutzer auf die ursprünglich vorgesehene URL nach dem erfolgreichen Zeichen Spedition

+0

Verwenden Sie stattdessen "window.location.replace" ('/ auth/login? Return_to =' + window.location.pathname); ' –

+0

Ich würde diese Antwort besser finden, wenn sie in JavaScript geschrieben wäre und nicht irgendeine Art zu schreiben Javascript, um schließlich zu Javascript kompiliert zu werden. – Catfish

0

Ich wäre glücklich zu sehen, ob es eine elegante Möglichkeit gibt, das auch zu tun!

Bis dahin, hier ist, wie ich es behandelt habe.

In Ihrer edit.js.erb View-Datei können Sie den folgenden den folgenden Code setzen:

<% case response.status 
    when 200 
%> 
    //do what you need to do 
<% when 401 %> 
    //handle the 401 case, for example by redirecting to root or something 
    window.location.href('/'); 
<% else %> 
    //catch all 
    alert('We\'ve had a problem, please close this, refresh the page and try again'); 
<% end %> 

Es befindet sich auf dem Statuscode der Antwort aussehen wird und auf die Anmeldeseite umleiten, wenn es 401.

Ich frage mich, ob es keine Möglichkeit gibt, dies direkt auf Controller-Ebene zu behandeln.

+1

Dank für die Idee. Es ist ähnlich zu meinem derzeitigen Ansatz, den ich von dieser Antwort bekommen habe: http://stackoverflow.com/questions/5460150/devise-with-rails-3-and-remote-true --- aber es gibt anscheinend keine erb Unterstützung in Dieser Fall, der es unmöglich macht, ein Rendering oder auch nur eine DRY-Umleitung zu 'new_user_session_path' oder ähnlichem zu machen. – user569825

1

Sie können die .Live für die Ereignisbindung "ajax: error" verwenden, wenn Sie ": remote => true" ausführen.

$('#member_invite, #new_user') 
     .live("ajax:success", function(evt, data, status, xhr){ 
      $.colorbox.close(); 
     }) 
     .live("ajax:error", function(evt, data, status, xhr){ 
      alert("got an error"); 
     }); 

wo der "#new_user" wäre der Formular-ID-Wert.

Beachten Sie, dass eine elegantere Art und Weise, wenn Sie bereits ein Overlay oder Dialog haben, ist einfach eine Nachricht einzufügen, so dass anstelle von alert():

$('.messages').html('Invalid email or password'); 

und in Ihrer signin Form Sie einfach ein

<div class="messages"></div> 

Oder Sie könnten auch nur den Titel des Formulars ersetzen, was auch immer Ihre Bedürfnisse sind.

+0

Danke für den Vorschlag. Ich glaube du hast verstanden, dass ich auf der Anmeldeseite im Beispiel bin. Bei der Frage handelt es sich jedoch um den Versuch, auf einen eingeschränkten Bereich zuzugreifen, während er nicht authentifiziert ist. Beachten Sie, dass ".live()" in jQuery 1.7 zugunsten von .on() 'veraltet ist. – user569825

14
$(document).ajaxError(function (e, xhr, settings) { 
     if (xhr.status == 401) { 
      $('.selector').html(xhr.responseText); 
     } 
    }); 
+1

Das funktionierte für mich - ich habe es in eine .js-Datei geschrieben, die auf jeder Seite aufgerufen wird, auf der ich Ajax-Submissions habe, aber ich habe $ ('.selector') .html (xhr.responseText);} durch location.reload ersetzt(); und das bringt Devise ins Leben, leitet zur normalen Anmeldeseite um und nach der Anmeldung bringt Sie wieder zurück – Mitch

+0

@Mitch: Ja. Das ist richtig –

2

Eine Version Mischereignisbindung mit location.reload().

$(function($) { 
    $("#new-user") 
    .bind("ajax:error", function(event, xhr, status, error) { 
     if (xhr.status == 401) { // probable Devise timeout 
     alert(xhr.responseText); 
     location.reload();  // reload whole page so Devise will redirect to signin 
     } 
    }); 
}); 

Testing mit Devise 3.1.1, dies setzt session["user_return_to"] korrekt, so dass der Benutzer nach der erneuten Anmeldung auf die Seite zurückkehrt().

ich die alert als einfache Weise hinzugefügt hier diskutierten die unelegant Nachricht Problem zu beheben: Session Timeout Message in RoR using Devise

+0

Ich sehe, wie dies als Workaround für Probleme in Bezug auf Timeouts dienen kann. Wenn sich das Anmeldeformular jedoch auf einer anderen Seite befindet, scheint dieser Code nicht zu diesem weitergeleitet zu werden. – user569825

+0

Meinst du, das Anmeldeformular befindet sich auf einer anderen Seite als die Standardseite? Ich habe das nicht versucht. '' Hake Routen | Grep signin' zurück etwas für dich? Meine geht zu 'devise/sessions # new', was meiner Meinung nach der Standard ist (obwohl ich es explizit aus anderen Gründen definiert habe). All dieser Code lädt die Basisseite neu, was Devise zwingen sollte, "** umzuleiten, da die nicht-remote Variante ** es tun würde." –

+1

Die Sache ist, wenn sich der Benutzer" gerade "auf einer Seite befindet, die ** mir ** erlaubt ist, auf unauthentifizierte zuzugreifen, aber auf einen Link zu einer Seite klickt, die ** ihn ** authentifizieren muss, der vorgeschlagen wird Lösung lädt nur die aktuelle, bereits zugängliche ** (!) ** Seite neu. Die Absicht bleibt für Devise unsichtbar und die Sitzungsnummer neu (oder Ähnliches) ist unberührt. Um beide Fälle abzudecken, sollten Sie auf die gewünschte Seite umleiten. Siehe "location.replace" in meiner Antwort. – user569825

2

Hier ist meine Kopie-past-hapy ist (tm) Lösung in Coffeescript. Es leitet alle 401 zur Anmeldeseite um.

<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %> 

$(document).ajaxError (_, xhr)-> 
    window.location = '<%= new_user_session_path %>' if xhr.status == 401 

und in Javascript:

<% environment.context_class.instance_eval { include Rails.application.routes.url_helpers } %> 

$(document).ajaxError(function(event, xhr){ 
    if (xhr.status == 401) { 
    window.location = '<%= new_user_session_path %>' 
    } 
}); 
Verwandte Themen