9 ответов:
не визуализируйте 404 самостоятельно, для этого нет причин; Rails уже имеет эту функциональность. Если вы хотите показать 404 страницу, Добавить
render_404метод (илиnot_foundкак я его назвал) вApplicationControllerтакой:def not_found raise ActionController::RoutingError.new('Not Found') endрельсы и ручки
AbstractController::ActionNotFoundиActiveRecord::RecordNotFoundтаким же образом.это делает две вещи лучше:
1) он использует рельсы ' встроенный
rescue_fromобработчик для отображения 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)и minitest:
assert_raises(ActionController::RoutingError) do get '/something/you/want/to/404' endили обратитесь за дополнительной информацией от Rails render 404 не найден из действия контроллера
HTTP-статус 404
чтобы вернуть заголовок 404, просто используйте для метода render.
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 endActiveRecord и HTTP 404
тоже помню это рельсы спасает некоторые ошибки 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
недавно выбранный ответ, представленный Стивеном Сорокой, близок, но не завершен. Сам тест скрывает тот факт, что это не возвращает истинный 404 - это возвращает статус 200 - "успех". Первоначальный ответ был ближе, но попытался отобразить макет, как будто никакого сбоя не произошло. Это исправляет все:
render :text => 'Not Found', :status => '404'вот типичный набор тестов для чего-то, что я ожидаю вернуть 404, используя RSpec и shoulda matchers:
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/
FYI: I не рекомендуется тестировать на то, что происходит в контроллере, т. е. "should_raise". То, что вас волнует, - это выход. Мои тесты выше позволили мне попробовать различные решения, и тесты остаются теми же, независимо от того, вызывает ли решение исключение, специальный рендеринг и т. д.
вы также можете использовать файл рендер:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404где вы можете использовать макет или нет.
другой вариант-использовать исключения для управления им:
raise ActiveRecord::RecordNotFound, "Record not found."
выбранный ответ не работает в 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 без переопределения основных функций рельсов.
это поможет вам...
Контроллер Приложения
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
<%= render file: 'public/404', status: 404, formats: [:html] %>просто добавьте это на страницу, которую вы хотите отобразить на странице ошибки 404, и все готово.
чтобы проверить обработку ошибок, вы можете сделать что-то вроде этого:
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
Если вы хотите обрабатывать разные 404s по-разному, подумайте о том, чтобы поймать их в своих контроллерах. Это позволит вам делать такие вещи, как отслеживание количества 404s, генерируемых различными группами пользователей, иметь поддержку взаимодействовать с пользователями, чтобы узнать, что пошло не так / какая часть пользовательского опыта может потребоваться настройка, выполнить 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
Comments