Вопрос:

Как перенаправить на 404 в Rails?

ruby-on-rails ruby http http-status-code-404

214906 просмотра

11 ответа

12539 Репутация автора

Я хотел бы «подделать» страницу 404 в Rails. В PHP я бы просто отправил заголовок с кодом ошибки как таковой:

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

Как это сделать с Rails?

Автор: Yuval Karmi Источник Размещён: 05.03.2010 09:52

Ответы (11)


239 плюса

149903 Репутация автора

HTTP 404 Статус

Чтобы вернуть заголовок 404, просто используйте :statusопцию для метода рендеринга.

def action
  # here the code

  render :status => 404
end

Если вы хотите отобразить стандартную страницу 404, вы можете извлечь функцию в методе.

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

и назовите это в вашем действии

def action
  # here the code

  render_404
end

Если вы хотите, чтобы действие отображало страницу ошибки и останавливалось, просто используйте оператор return.

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

  # here the code that will never be executed
end

ActiveRecord и HTTP 404

Также помните, что Rails спасает некоторые ошибки ActiveRecord, такие как ActiveRecord::RecordNotFoundотображение страницы с ошибкой 404.

Это означает, что вам не нужно спасать это действие самостоятельно

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

User.findвызывает, ActiveRecord::RecordNotFoundкогда пользователь не существует. Это очень мощная функция. Посмотрите на следующий код

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

Вы можете упростить это, делегировав Rails проверку. Просто используйте версию взрыва.

def show
  user = User.find_by_email!(params[:email])
  # ...
end
Автор: Simone Carletti Размещён: 05.03.2010 09:57

1033 плюса

16670 Репутация автора

Решение

Не визуализируйте 404 самостоятельно, нет причин для этого; В Rails эта функциональность уже встроена. Если вы хотите показать страницу 404, создайте render_404метод (или not_foundкак я его назвал) ApplicationControllerследующим образом:

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

Rails тоже обрабатывает AbstractController::ActionNotFound, и так ActiveRecord::RecordNotFoundже.

Это делает две вещи лучше:

1) Он использует встроенный rescue_fromобработчик Rails для рендеринга страницы 404, и 2) он прерывает выполнение вашего кода, позволяя вам делать такие приятные вещи, как:

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

без необходимости писать некрасивые условные высказывания.

В качестве бонуса, это также очень легко обрабатывать в тестах. Например, в тесте интеграции 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)

И минитест:

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

ИЛИ см. Дополнительную информацию из рендера Rails 404, не найденного в действии контроллера

Автор: Steven Soroka Размещён: 13.02.2011 09:09

58 плюса

21259 Репутация автора

Недавно выбранный ответ, представленный Стивеном Сорокой, близок, но не завершен. Сам тест скрывает тот факт, что это не возвращает истинный 404 - он возвращает статус 200 - «успех». Первоначальный ответ был ближе, но попытался отобразить макет так, как если бы не произошло никакого сбоя. Это исправляет все:

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

Вот типичный мой тестовый набор для чего-то, что я ожидаю вернуть 404, используя сопоставители RSpec и 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

Эта здоровая паранойя позволила мне обнаружить несоответствие типов контента, когда все остальное выглядело замечательно :) Я проверяю все эти элементы: назначенные переменные, код ответа, тип контента ответа, представленный шаблон, отображаемый макет, флэш-сообщения.

Я пропущу проверку типа контента для приложений, которые строго HTML ... иногда. Ведь «скептик проверяет ВСЕ ящики» :)

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

К вашему сведению: я не рекомендую проверять то, что происходит в контроллере, то есть «should_raise». Что вас волнует, так это результат. Мои тесты, приведенные выше, позволили мне попробовать различные решения, и тесты остаются прежними, независимо от того, вызывает ли решение исключение, специальный рендеринг и т. Д.

Автор: Jaime Bellmyer Размещён: 17.08.2011 08:40

12 плюса

8293 Репутация автора

Выбранный ответ не работает в Rails 3.1+, так как обработчик ошибок был перемещен в промежуточное ПО (см. Проблему с github ).

Вот решение, которое я нашел, которым я очень доволен.

В 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

и в application.rb:

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

И в моих ресурсах (показать, отредактировать, обновить, удалить):

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

Это, безусловно, можно улучшить, но, по крайней мере, у меня есть разные представления для not_found и internal_error без переопределения основных функций Rails.

Автор: Augustin Riedinger Размещён: 12.03.2014 03:16

7 плюса

1238 Репутация автора

это поможет вам ...

Контроллер приложений

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

Контроллер ошибок

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

просмотров / ошибки / 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
Автор: Caner Çakmak Размещён: 01.04.2014 07:54

0 плюса

9407 Репутация автора

Чтобы проверить обработку ошибок, вы можете сделать что-то вроде этого:

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
Автор: maprihoda Размещён: 14.04.2014 08:40

18 плюса

16978 Репутация автора

Вы также можете использовать файл рендеринга:

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

Где вы можете использовать макет или нет.

Другой вариант - использовать исключения для управления им:

raise ActiveRecord::RecordNotFound, "Record not found."
Автор: Paulo Fidalgo Размещён: 19.01.2015 03:41

0 плюса

746 Репутация автора

Если вы хотите обрабатывать разные 404 по-разному, подумайте о том, чтобы перехватить их в своих контроллерах. Это позволит вам выполнять такие операции, как отслеживание числа 404, сгенерированного различными группами пользователей, поддержка взаимодействия с пользователями, чтобы выяснить, что пошло не так / какая часть пользовательского опыта может потребоваться настроить, выполнить A / B-тестирование и т. Д.

Здесь я поместил базовую логику в ApplicationController, но она также может быть помещена в более конкретные контроллеры, чтобы иметь специальную логику только для одного контроллера.

Причина, по которой я использую if с ENV ['RESCUE_404'], заключается в том, что я могу проверить поднятие AR :: RecordNotFound изолированно. В тестах я могу установить для этого ENV var значение false, и мой rescue_from не сработает. Таким образом, я могу проверить повышение отдельно от условной логики 404.

class ApplicationController < ActionController::Base

  rescue_from ActiveRecord::RecordNotFound, with: :conditional_404_redirect if ENV['RESCUE_404']

private

  def conditional_404_redirect
    track_404(@current_user)
    if @current_user.present?
      redirect_to_user_home          
    else
      redirect_to_front
    end
  end

end
Автор: Houen Размещён: 25.07.2015 07:15

1 плюс

143 Репутация автора

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

просто добавьте это на страницу, которую вы хотите отобразить на странице ошибки 404, и все готово.

Автор: Ahmed Reza Размещён: 01.11.2015 06:42

1 плюс

1044 Репутация автора

Я хотел выдать «нормальный» 404 для любого вошедшего в систему пользователя, который не является администратором, поэтому я написал что-то подобное в Rails 5:

class AdminController < ApplicationController
  before_action :blackhole_admin

  private

  def blackhole_admin
    return if current_user.admin?

    raise ActionController::RoutingError, 'Not Found'
  rescue ActionController::RoutingError
    render file: "#{Rails.root}/public/404", layout: false, status: :not_found
  end
end
Автор: emptywalls Размещён: 04.11.2018 08:05

0 плюса

1 Репутация автора

routes.rb
  get '*unmatched_route', to: 'main#not_found'

main_controller.rb
  def not_found
    render :file => "#{Rails.root}/public/404.html", :status => 404, :layout => false
  end
Автор: Arkadiusz Mazur Размещён: 25.08.2019 08:10
Вопросы из категории :
32x32