76 votes

Nécessité de renvoyer une erreur 404 formatée en JSON dans Rails

J'ai un front-end HTML normal et une API JSON dans mon application Rails. Maintenant, si quelqu'un appelle /api/not_existent_method.json il renvoie la page HTML 404 par défaut. Y a-t-il un moyen de changer cela en quelque chose comme {"error": "not_found"} tout en laissant intacte la page 404 originale pour le frontend HTML ?

111voto

iblue Points 10472

Un ami m'a orienté vers une solution élégante qui gère non seulement les erreurs 404 mais aussi 500. En fait, elle traite toutes les erreurs. La clé est que chaque erreur génère une exception qui se propage vers le haut de la pile de middlewares de rack jusqu'à ce qu'elle soit traitée par l'un d'entre eux. Si vous souhaitez en savoir plus, vous pouvez regarder cet excellent screencast . Rails possède ses propres gestionnaires d'exceptions, mais vous pouvez les remplacer par l'outil moins documenté exceptions_app option de configuration. Maintenant, vous pouvez écrire votre propre intergiciel ou vous pouvez renvoyer l'erreur dans rails, comme ceci :

# In your config/application.rb
config.exceptions_app = self.routes

Ensuite, il suffit de faire correspondre ces routes dans votre config/routes.rb :

match "/404" => "errors#not_found"
match "/500" => "errors#exception"

Et ensuite vous créez un contrôleur pour gérer ça.

class ErrorsController < ActionController::Base
  def not_found
    if env["REQUEST_PATH"] =~ /^\/api/
      render :json => {:error => "not-found"}.to_json, :status => 404
    else
      render :text => "404 Not found", :status => 404 # You can render your own template here
    end
  end

  def exception
    if env["REQUEST_PATH"] =~ /^\/api/
      render :json => {:error => "internal-server-error"}.to_json, :status => 500
    else
      render :text => "500 Internal Server Error", :status => 500 # You can render your own template here
    end
  end
end

Une dernière chose à ajouter : dans l'environnement de développement, rails ne rend généralement pas les pages 404 ou 500 mais imprime un backtrace à la place. Si vous voulez voir votre ErrorsController en action dans le mode de développement, puis désactivez le backtrace dans vos config/enviroments/development.rb fichier.

config.consider_all_requests_local = false

13voto

Greg Funtusov Points 505

J'aime créer un contrôleur d'API distinct qui définit le format (json) et les méthodes spécifiques à l'API :

class ApiController < ApplicationController
  respond_to :json

  rescue_from ActiveRecord::RecordNotFound, with: not_found
  # Use Mongoid::Errors::DocumentNotFound with mongoid

  def not_found
    respond_with '{"error": "not_found"}', status: :not_found
  end
end

Test RSpec :

  it 'should return 404' do
    get "/api/route/specific/to/your/app/", format: :json
    expect(response.status).to eq(404)
  end

11voto

bioneuralnet Points 3407

Bien sûr, ça ressemblera à quelque chose comme ça :

class ApplicationController < ActionController::Base
  rescue_from NotFoundException, :with => :not_found
  ...

  def not_found
    respond_to do |format|
      format.html { render :file => File.join(Rails.root, 'public', '404.html') }
      format.json { render :text => '{"error": "not_found"}' }
    end
  end
end

NotFoundException est pas le nom réel de l'exception. Il varie en fonction de la version de Rails et du comportement exact que vous souhaitez. Il est assez facile à trouver avec une recherche Google.

5voto

jdoe Points 10394

Essayez de mettre à la fin de votre routes.rb :

match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X