521 Stimmen

Wie man auf eine 404 in Rails umleiten?

Ich möchte eine 404-Seite in Rails "fälschen". In PHP würde ich einfach einen Header mit dem Fehlercode als solchen senden:

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

Wie wird das mit Rails gemacht?

1100voto

Steven Soroka Punkte 18749

Rendere 404 nicht selbst, es gibt keinen Grund dazu; Rails hat diese Funktionalität bereits eingebaut. Wenn Sie eine 404-Seite anzeigen wollen, erstellen Sie eine render_404 Methode (oder not_found wie ich es nannte) in ApplicationController wie diese:

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

Rails verarbeitet auch AbstractController::ActionNotFound y ActiveRecord::RecordNotFound auf die gleiche Weise.

Das macht zwei Dinge besser:

1) Es verwendet die in Rails eingebaute rescue_from Handler, um die 404-Seite zu rendern, und 2) es unterbricht die Ausführung Ihres Codes, so dass Sie nette Dinge tun können wie:

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

ohne hässliche bedingte Anweisungen schreiben zu müssen.

Außerdem ist es bei Tests sehr einfach zu handhaben. Zum Beispiel in einem rspec-Integrationstest:

# 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)

Und Minitest:

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

ODER beziehen Sie weitere Informationen von Rails render 404 nicht gefunden von einer Controller-Aktion

266voto

Simone Carletti Punkte 168374

HTTP 404 Status

Um einen 404-Header zurückzugeben, verwenden Sie einfach die :status Option für die Rendering-Methode.

def action
  # here the code

  render :status => 404
end

Wenn Sie die 404-Standardseite rendern möchten, können Sie das Merkmal in einer Methode extrahieren.

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

und rufen Sie es in Ihrer Aktion auf

def action
  # here the code

  render_404
end

Wenn Sie möchten, dass die Aktion die Fehlerseite wiedergibt und beendet wird, verwenden Sie einfach eine return-Anweisung.

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

  # here the code that will never be executed
end

ActiveRecord und HTTP 404

Denken Sie auch daran, dass Rails einige ActiveRecord-Fehler rettet, wie zum Beispiel die ActiveRecord::RecordNotFound die Anzeige der 404-Fehlerseite.

Das bedeutet, dass Sie diese Aktion nicht selbst retten müssen.

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

User.find wirft ein ActiveRecord::RecordNotFound wenn der Benutzer nicht existiert. Dies ist eine sehr leistungsfähige Funktion. Sehen Sie sich den folgenden Code an

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

Sie können dies vereinfachen, indem Sie die Prüfung an Rails delegieren. Verwenden Sie einfach die Bang-Version.

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

22voto

Paulo Fidalgo Punkte 20626

Sie können auch die Renderdatei verwenden:

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

Hier können Sie wählen, ob Sie das Layout verwenden möchten oder nicht.

Eine andere Möglichkeit ist die Verwendung der Ausnahmen zur Kontrolle:

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

16voto

Augustin Riedinger Punkte 18556

Die gewählte Antwort funktioniert nicht in Rails 3.1+, da der Error-Handler in eine Middleware verschoben wurde (siehe Github-Problem ).

Hier ist die Lösung, die ich gefunden habe und mit der ich ziemlich zufrieden bin.

En 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

und in application.rb :

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

Und in meinen Ressourcen (anzeigen, bearbeiten, aktualisieren, löschen):

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

Dies könnte sicherlich verbessert werden, aber zumindest habe ich verschiedene Ansichten für not_found und internal_error, ohne die Kernfunktionen von Rails zu überschreiben.

7voto

Caner Punkte 1530

Diese werden Ihnen helfen...

Anwendung Controller

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

Fehler-Controller

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X