2011-02-01 15 views
46

Sto cercando di costruire un API rotaie per un app iPhone. Devise funziona bene per gli accessi tramite l'interfaccia web ma devo essere in grado di creare e distruggere sessioni usando l'API REST e voglio usare JSON invece di dover fare un POST sul controller di sessioni e dover analizzare l'HTML e gestire un reindirizzare.Estendere Devise SessionsController di autenticarsi utilizzando JSON

ho pensato che avrei potuto fare qualcosa di simile:

class Api::V1::SessionsController < Devise::SessionsController 
    def create 
    super 
    end 
    def destroy 
    super 
    end 
end 

e in config/routes.rb ho aggiunto:

namespace :api do 
    namespace :v1 do 
    resources :sessions, :only => [:create, :destroy] 
    end 
end 

rake routes mostra i percorsi sono configurato correttamente:

api_v1_sessions POST /api/v1/sessions(.:format)  {:action=>"create", :controller=>"api/v1/sessions"} 
    api_v1_session DELETE /api/v1/sessions/:id(.:format) {:action=>"destroy", :controller=>"api/v1/sessions"} 

Quando I POST a/utente/sessioni tutto funziona correttamente. Ho un po 'HTML e un 302.

Ora, se ho posto a/API/v1/sessioni ottengo:

Unknown azione AbstractController :: ActionNotFound

curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v1/sessions -d "{'user' : { 'login' : 'test', 'password' : 'foobar'}}" 
+0

Questo sarà anche lavoro ed è molto più semplice: http://stackoverflow.com/a/22582952/2555008 – Sanjeev

risposta

39

Questo è quello che alla fine ha funzionato.

class Api::V1::SessionsController < Devise::SessionsController 
    def create 
    respond_to do |format| 
     format.html { super } 
     format.json { 
     warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new") 
     render :status => 200, :json => { :error => "Success" } 
     } 
    end 
    end 
    def destroy 
    super 
    end 
end 

Cambiare anche routes.rb, ricorda che l'ordine è importante.

devise_for :users, :controllers => { :sessions => "api/v1/sessions" } 
devise_scope :user do 
    namespace :api do 
    namespace :v1 do 
     resources :sessions, :only => [:create, :destroy] 
    end 
    end 
end 

resources :users 
+1

potresti approfondire un po 'di più? Stai passando login e password per l'autenticazione? –

+0

Una cosa da aggiungere a questa: inizialmente avevo una vista di login personalizzata sotto app/views/devise/sessions/new.html.erb. Ho dovuto spostare questo in una nuova cartella di visualizzazione per abbinare il nome del controller personalizzato/spazio dei nomi. –

1

Dalla rdoc per #devise_scope di escogitare:

imposta l'ambito escogitare da utilizzare nel controllore. Se si dispone di percorsi personalizzati, si sono tenuti a chiamare questo metodo (alias anche come: come) per specificare a quale controllore si è mirato.

as :user do 
    get "sign_in", :to => "devise/sessions#new" 
end 

Avviso non si possono avere due ambiti mappatura allo stesso URL. E ricorda, se si tenta di accedere a un controller di sviluppo senza specificare un ambito, sarà aumentare l'errore ActionNotFound.

Sembra che tu abbia bisogno di avvolgerla in un blocco #as:

as :user do 
    namespace :api do 
    namespace :v1 do 
     resources :sessions, :only => [:create, :destroy] 
    end 
    end 
end 
+0

Secondo questo URL https://github.com/plataformatec/devise/wiki/How-To: -Change-the-default-sign_in-and-sign_out-routes Il modo in cui ha posato il suo codice è corretto. Anch'io sto affrontando lo stesso insetto di lui. –

+0

No, non lo è. L'rdoc afferma chiaramente che se si sottoclasse un controller di devise, la route in cui viene chiamata deve essere racchiusa in un blocco #as o #devise_scope, altrimenti si otterrà un errore ActionNotFound. –

1

Una soluzione alternativa alla creazione/distruzione di sessioni è quello di utilizzare il modulo di Devise token_authenticatable, e quindi aggiornare le altre funzioni nella vostra API in modo che prendono il token come parametro obbligatorio. Questo è probabilmente un disegno più aggiornato, dal momento che mantiene l'apolidia (cioè, non c'è stato di sessione da nessuna parte). Ovviamente, questo consiglio vale per la tua API JSON, ma non raccomanderei lo stesso per la tua interfaccia utente HTML (le stringhe dei token lunghi nella barra degli URL del tuo browser non sono una bella vista).

Vedi here per un esempio.

+1

Questo è stato rimosso da Devise nella v3.1.0, FWIW. http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/ – turboladen

10

Ho finito per utilizzare una combinazione della risposta di @ akshay e la risposta di @ mm2001.

class Api::SessionsController < Devise::SessionsController 
    def create 
    warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure") 
    render :json => {:success => true} 
    end 

    def destroy 
    Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name) 
    render :json => {} 
    end 

    def failure 
    render :json => {:success => false, :errors => ["Login Failed"]} 
    end 
end 

... e nel inizializzatore disposizione testamentaria, ho dovuto fare questo per ottenere il metodo #create di utilizzare il mio gestore :recall

# config/initializers/devise.rb 
config.navigational_formats = [:"*/*", "*/*", :html, :json] 

Questo è con Devise 1.5.1 e Rails 3.1.

+4

+1 per le informazioni su 'config.navigational_formats'. Una cosa che potrebbe renderla più robusta nel caso in cui dovessi aggiornare idea; Puoi semplicemente scavalcare Devise :: SessionsController.auth_options per restituire il tuo metodo di errore e quindi utilizzare le implementazioni predefinite per tutto il resto invece di implementare crea e distruggi te stesso (supponendo che la risposta predefinita sia sufficiente). 'def auth_options super.merge ({: recall =>" # {controller_path} #failure "}) end' – plainjimbo

+1

Oh, a proposito, sono su devise 2.0.4 e rails 3.2. In modo che possa influenzare se altri possono fare quello che ho fatto. – plainjimbo

+0

Fantastico, grazie @Jimbo. Sembra un po 'più pulito. – declan

0

Non sono sicuro di navigazione-formati dovrebbero essere usati, un API non è poi così ...

Per questo blog post basta aggiungere

respond_to :html, :json 

ai controller.

Problemi correlati