2013-04-04 9 views
7

Sto cercando di visualizzare un messaggio di errore in una vista di jbuilder. Per esempio, un percorso che potrei avere potrebbe essere:Rails modo corretto per visualizzare l'errore con jbuilder

/foos/:id/bars

Se :id presentata dal utente non esiste o non è valido, mi piacerebbe essere in grado di visualizzare il messaggio di errore di conseguenza nel mio file index.json.builder .

Utilizzando Rails, qual è il modo migliore per ottenere questo risultato? Il controller potrebbe avere qualcosa come:

def index 
    @bar = Bar.where(:foo_id => params[:id]) 
end 

In questo caso, potrebbe essere params[:id]nil, o quell'oggetto potrebbe non esistere. Non sono sicuro che la cosa migliore da fare qui sia gestirla nel controller e rendere esplicitamente un error.json.builder, o gestirlo nella vista index.json.builder stessa. Qual è il modo corretto per farlo e se è nello index.json.builder, è disponibile il params[:id]? So che posso vedere se @bar.nil? ma non sono sicuro sull'inverso?

risposta

4

vorrei rendere index.json.builder o JSON semplicemente in linea con :error => 'not found' e non dimenticare di impostare il corretto stato HTTP: :status => 404

Così risultato potrebbe assomigliare a questo:

render :json => { :error => 'not found' }, :status => 422 if @bar.nil? 
4

I pensa che tu volessi mostrare, dato che l'indice è davvero per liste/collezioni. E dovresti prendere .first dove, altrimenti hai solo una relazione, giusto? Quindi, utilizza .first! per generare un errore, poiché il middleware Rack di Rails in Rails 4 verrà gestito in modo basico, ad es.

def show 
    # need to do to_s on params value if affected by security issue CVE-2013-1854 
    @bar = Bar.where(:foo_id => params[:id].to_s).first! 
end 

È anche possibile utilizzare @bar = Bar.find(params[:id]), ma che è deprecato e sarà rimosso in Rails 4.1, dopo di che si dovrà aggiungere al vostro gem 'activerecord-deprecated_finders' Gemfile da usare.

Per l'indice, probabilmente si desidera @bars = Bar.all. Se per qualche motivo si desidera filtrare e non si desidera l'ambito, ecc., È possibile utilizzare @bars = Bar.where(...).to_a o simile.

Rails 4: Gestione delle eccezioni di base nel rack è automatico

Finché la query prende il via un errore, le rotaie 4 dovrebbe essere in grado di restituire la parte del messaggio di errore per qualsiasi formato supportato where to_(format) può essere chiamato su un hash (es. json, xml, ecc.).

Per capire perché, dai un'occhiata a Rails Rack public_exceptions middleware.

Se è html, proverà a leggere il file correlato dalla directory pubblica in Rails per il codice di stato (ad esempio 500.html per un errore del server/HTTP 500).

Se è un altro formato, proverà a eseguire to_(the format) sull'hash: { :status => status, :error => exception.message }.Per vedere come questo dovrebbe funzionare andare a Rails console:

$ rails c 
... 
1.9.3p392 :001 > {status: 500, error: "herro shraggy!"}.to_xml 
=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n <status type=\"integer\">500</status>\n <error>herro shraggy!</error>\n</hash>\n" 
1.9.3p392 :002 > {status: 500, error: "herro shraggy!"}.to_json 
=> "{\"status\":500,\"error\":\"herro shraggy!\"}" 

Nel middleware, vedrete il X-Cascade intestazione nel codice e in vari luoghi legati alla Rails' la gestione delle eccezioni in rack. Per this answer, l'intestazione X-Cascade è impostata su pass per indicare a Rack di provare altri percorsi per trovare una risorsa.

Rails 3.2.x: può gestire le eccezioni in rack

In Rails 3.2.x, che il codice per fare to_(format) per il corpo di risposta, ecc non è in public_exceptions.rb. Gestisce solo il formato html.

Forse potresti provare a sostituire il vecchio middleware con la versione più recente tramite una patch.

Se preferisci che Rack gestisca il tuo errore in modo più specifico senza patch, vedi # 3 nel post di José Valim, "My five favorite “hidden” features in Rails 3.2".

In questo e come dice anche another answer, è possibile utilizzare config.exceptions_app = self.routes. Quindi con percorsi che puntano a un controller personalizzato, puoi gestire gli errori da qualsiasi controller come qualsiasi altra richiesta. Nota il bit su config.consider_all_requests_local = false nel tuo config/environments/development.rb.

Non è necessario utilizzare i percorsi per utilizzare exceptions_app. Anche se può essere un po 'intimidatorio, è solo un proc/lambda che accetta un hash e restituisce un array il cui formato è: [http_status_code_number, {headers hash...}, ['the response body']]. Per esempio, si dovrebbe essere in grado di fare questo nel vostro Rails 3.2.x di configurazione per rendere più gestire gli errori come Rails 4.0 (questo è l'ultimo public_exceptions middleware crollato):

config.exceptions_app = lambda do |env| 
    exception = env["action_dispatch.exception"] 
    status = env["PATH_INFO"][1..-1] 
    request = ActionDispatch::Request.new(env) 
    content_type = request.formats.first 
    body = { :status => status, :error => exception.message } 
    format = content_type && "to_#{content_type.to_sym}" 
    if format && body.respond_to?(format) 
    formatted_body = body.public_send(format) 
    [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}", 
      'Content-Length' => body.bytesize.to_s}, [formatted_body]] 
    else 
    found = false 
    path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale 
    path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path)) 

    if found || File.exist?(path) 
     [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}", 
       'Content-Length' => body.bytesize.to_s}, [File.read(path)]] 
    else 
     [404, { "X-Cascade" => "pass" }, []] 
    end 
    end 
end 

Nota: Per qualsiasi problema di che la gestione , l'implementazione failsafe è in ActionDispatch::ShowExceptionshere.

Rails 3 e 4: Gestione delle eccezioni in Rails controller

Se si preferisce avere il rendering di errore nel controller stesso, si può fare:

def show 
    respond_with @bar = Bar.where(:foo_id => params[:id].to_s).first! 
rescue ActiveRecord::RecordNotFound => e 
    respond_to do |format| 
    format.json => { :error => e.message }, :status => 404 
    end 
end 

Ma, non avete bisogno per aumentare gli errori. Si potrebbe anche fare:

def show 
    @bar = Bar.where(:foo_id => params[:id].to_s).first 
    if @bar 
    respond_with @bar 
    else 
    respond_to do |format| 
     format.json => { :error => "Couldn't find Bar with id=#{params[:id]}" }, :status => 404 
    end 
    end 
end 

È inoltre possibile utilizzare rescue_from, per esempio nel controller, o ApplicationController, ecc .:

rescue_from ActiveRecord::RecordNotFound, with: :not_found 

def not_found(exception) 
    respond_to do |format| 
    format.json => { :error => e.message }, :status => 404 
    end 
end 

o:

rescue_from ActiveRecord::RecordNotFound do |exception| 
    respond_to do |format| 
    format.json => { :error => e.message }, :status => 404 
    end 
end 

Anche se alcuni errori comuni possono essere gestiti nel controllore, se gli errori relativi alle rotte mancanti, ecc formattato in JSON , ecc., quelli che devono essere gestiti nel middleware Rack.

Problemi correlati