Question Comment rediriger vers un 404 dans Rails?


Je voudrais "simuler" une page 404 dans Rails. En PHP, je voudrais juste envoyer un en-tête avec le code d'erreur en tant que tel:

header("HTTP/1.0 404 Not Found");

Comment cela se fait-il avec Rails?


441
2018-03-05 09:52


origine


Réponses:


Ne rends pas 404 toi-même, il n'y a pas de raison de le faire; Rails a déjà cette fonctionnalité intégrée. Si vous voulez afficher une page 404, créez un render_404 méthode (ou not_found comme je l'ai appelé) dans ApplicationController comme ça:

def not_found
  raise ActionController::RoutingError.new('Not Found')
end

Rails gère également AbstractController::ActionNotFound, et ActiveRecord::RecordNotFound de la même façon.

Cela fait deux choses mieux:

1) Il utilise Rails 'intégré rescue_from gestionnaire pour rendre la page 404, et 2) il interrompt l'exécution de votre code, vous permettant de faire de belles choses comme:

  user = User.find_by_email(params[:email]) or not_found
  user.do_something!

sans avoir à écrire des instructions conditionnelles laides.

En prime, il est également très facile à manipuler dans les tests. Par exemple, dans un test d'intégration rspec:

# RSpec 1

lambda {
  visit '/something/you/want/to/404'
}.should raise_error(ActionController::RoutingError)

# RSpec 2+

expect {
  get '/something/you/want/to/404'
}.to raise_error(ActionController::RoutingError)

Et minitest:

assert_raises(ActionController::RoutingError) do 
  get '/something/you/want/to/404'
end

997
2018-02-13 09:09



Statut HTTP 404

Pour retourner un en-tête 404, utilisez simplement le :status option pour la méthode de rendu.

def action
  # here the code

  render :status => 404
end

Si vous souhaitez afficher la page 404 standard, vous pouvez extraire la fonctionnalité dans une méthode.

def render_404
  respond_to do |format|
    format.html { render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found }
    format.xml  { head :not_found }
    format.any  { head :not_found }
  end
end

et appelez-le dans votre action

def action
  # here the code

  render_404
end

Si vous souhaitez que l'action affiche la page d'erreur et s'arrête, utilisez simplement une instruction return.

def action
  render_404 and return if params[:something].blank?

  # here the code that will never be executed
end

ActiveRecord et HTTP 404

Rappelez-vous également que Rails sauve certaines erreurs ActiveRecord, telles que ActiveRecord::RecordNotFound afficher la page d'erreur 404.

Cela signifie que vous n'avez pas besoin de sauver cette action vous-même

def show
  user = User.find(params[:id])
end

User.find soulève un ActiveRecord::RecordNotFound lorsque l'utilisateur n'existe pas. C'est une fonctionnalité très puissante. Regardez le code suivant

def show
  user = User.find_by_email(params[:email]) or raise("not found")
  # ...
end

Vous pouvez le simplifier en déléguant le chèque à Rails. Utilisez simplement la version bang.

def show
  user = User.find_by_email!(params[:email])
  # ...
end

230
2018-03-05 09:57



La nouvelle réponse sélectionnée par Steven Soroka est proche, mais pas complète. Le test lui-même cache le fait que cela ne renvoie pas un vrai 404 - il retourne un statut de 200 - "succès". La réponse initiale était plus proche, mais a tenté de rendre la mise en page comme si aucune défaillance ne s'était produite. Cela corrige tout:

render :text => 'Not Found', :status => '404'

Voici un jeu de test typique pour quelque chose que je compte retourner 404, en utilisant les matchers RSpec et Shoulda:

describe "user view" do
  before do
    get :show, :id => 'nonsense'
  end

  it { should_not assign_to :user }

  it { should respond_with :not_found }
  it { should respond_with_content_type :html }

  it { should_not render_template :show }
  it { should_not render_with_layout }

  it { should_not set_the_flash }
end

Cette paranoïa saine m'a permis de repérer l'inadéquation de type de contenu quand tout le reste semblait peachy :) Je vérifie tous ces éléments: variables assignées, code de réponse, type de contenu de réponse, modèle rendu, disposition rendue, messages instantanés.

Je vais sauter le contrôle de type de contenu sur les applications qui sont strictement HTML ... parfois. Après tout, "un sceptique vérifie TOUS les tiroirs" :)

http://dilbert.com/strips/comic/1998-01-20/

FYI: Je ne recommande pas de tester les choses qui se passent dans le contrôleur, par exemple "should_raise". Ce qui compte pour vous, c'est la sortie. Mes tests ci-dessus m'ont permis d'essayer différentes solutions, et les tests restent les mêmes que la solution soit une exception, un rendu spécial, etc.


56
2017-08-17 20:40



Vous pouvez également utiliser le fichier de rendu:

render file: "#{Rails.root}/public/404.html", layout: false, status: 404

Où vous pouvez choisir d'utiliser la mise en page ou non.

Une autre option consiste à utiliser les exceptions pour le contrôler:

raise ActiveRecord::RecordNotFound, "Record not found."

14
2018-01-19 15:41



La réponse sélectionnée ne fonctionne pas dans Rails 3.1+ car le gestionnaire d’erreur a été déplacé vers un middleware (voir numéro github).

Voici la solution que j’ai trouvée et que je suis plutôt satisfaite.

Dans ApplicationController:

  unless Rails.application.config.consider_all_requests_local
    rescue_from Exception, with: :handle_exception
  end

  def not_found
    raise ActionController::RoutingError.new('Not Found')
  end

  def handle_exception(exception=nil)
    if exception
      logger = Logger.new(STDOUT)
      logger.debug "Exception Message: #{exception.message} \n"
      logger.debug "Exception Class: #{exception.class} \n"
      logger.debug "Exception Backtrace: \n"
      logger.debug exception.backtrace.join("\n")
      if [ActionController::RoutingError, ActionController::UnknownController, ActionController::UnknownAction].include?(exception.class)
        return render_404
      else
        return render_500
      end
    end
  end

  def render_404
    respond_to do |format|
      format.html { render template: 'errors/not_found', layout: 'layouts/application', status: 404 }
      format.all { render nothing: true, status: 404 }
    end
  end

  def render_500
    respond_to do |format|
      format.html { render template: 'errors/internal_server_error', layout: 'layouts/application', status: 500 }
      format.all { render nothing: true, status: 500}
    end
  end

et en application.rb:

config.after_initialize do |app|
  app.routes.append{ match '*a', :to => 'application#not_found' } unless config.consider_all_requests_local
end

Et dans mes ressources (show, edit, update, delete):

@resource = Resource.find(params[:id]) or not_found

Cela pourrait certainement être amélioré, mais au moins, j'ai des vues différentes pour not_found et internal_error sans surcharger les fonctions de base de Rails.


11
2018-03-12 15:16



ceux-ci vous aideront ...

Contrôleur d'application 

class ApplicationController < ActionController::Base
  protect_from_forgery
  unless Rails.application.config.consider_all_requests_local             
    rescue_from ActionController::RoutingError, ActionController::UnknownController, ::AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, with: lambda { |exception| render_error 404, exception }
  end

  private
    def render_error(status, exception)
      Rails.logger.error status.to_s + " " + exception.message.to_s
      Rails.logger.error exception.backtrace.join("\n") 
      respond_to do |format|
        format.html { render template: "errors/error_#{status}",status: status }
        format.all { render nothing: true, status: status }
      end
    end
end

Contrôleur d'erreurs

class ErrorsController < ApplicationController
  def error_404
    @not_found_path = params[:not_found]
  end
end

views / errors / error_404.html.haml

.site
  .services-page 
    .error-template
      %h1
        Oops!
      %h2
        404 Not Found
      .error-details
        Sorry, an error has occured, Requested page not found!
        You tried to access '#{@not_found_path}', which is not a valid page.
      .error-actions
        %a.button_simple_orange.btn.btn-primary.btn-lg{href: root_path}
          %span.glyphicon.glyphicon-home
          Take Me Home

7
2018-04-01 19:54



<%= render file: 'public/404', status: 404, formats: [:html] %>

ajoutez simplement ceci à la page que vous voulez rendre à la page d'erreur 404 et vous avez terminé.


1
2017-11-01 06:42



Pour tester la gestion des erreurs, vous pouvez faire quelque chose comme ceci:

feature ErrorHandling do
  before do
    Rails.application.config.consider_all_requests_local = false
    Rails.application.config.action_dispatch.show_exceptions = true
  end

  scenario 'renders not_found template' do
    visit '/blah'
    expect(page).to have_content "The page you were looking for doesn't exist."
  end
end

0
2018-04-14 20:40