我想在Rails中“伪造”一个404页面。在PHP中,我只会发送一个带有错误代码的报头:
header("HTTP/1.0 404 Not Found");
Rails如何做到这一点?
我想在Rails中“伪造”一个404页面。在PHP中,我只会发送一个带有错误代码的报头:
header("HTTP/1.0 404 Not Found");
Rails如何做到这一点?
当前回答
Raising ActionController::RoutingError('not found')对我来说总是感觉有点奇怪——在未经过身份验证的用户的情况下,这个错误并不反映现实——路由被找到了,用户只是没有经过身份验证。
我偶然发现config。action_dispatch。我认为在某些情况下,这是一个更优雅的解决方案的问题:
# application.rb
config.action_dispatch.rescue_responses = {
'UnauthenticatedError' => :not_found
}
# my_controller.rb
before_action :verify_user_authentication
def verify_user_authentication
raise UnauthenticatedError if !user_authenticated?
end
这种方法的好处在于:
它像普通的ActionController::RoutingError一样挂钩到现有的错误处理中间件中,但是在开发环境中你会得到一个更有意义的错误消息 它将正确地将状态设置为您在rescue_responses散列中指定的任何状态(在本例中为404 - not_found) 您不必编写一个需要在任何地方都可用的not_found方法。
其他回答
你也可以使用渲染文件:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
你可以选择是否使用该布局。
另一种选择是使用Exceptions来控制它:
raise ActiveRecord::RecordNotFound, "Record not found."
为了测试错误处理,你可以这样做:
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
<%= render file: 'public/404', status: 404, formats: [:html] %>
只需将此添加到要呈现到404错误页面的页面,就完成了。
如果希望以不同的方式处理不同的404,可以考虑在控制器中捕获它们。这将允许你做一些事情,如跟踪不同用户组生成的404数量,让支持人员与用户交互,找出哪里出了问题/用户体验的哪一部分可能需要调整,进行A/B测试等。
我在这里将基本逻辑放在ApplicationController中,但它也可以放在更特定的控制器中,以便只针对一个控制器拥有特殊的逻辑。
我使用ENV['RESCUE_404']的if的原因是,这样我就可以单独测试AR::RecordNotFound的提升。在测试中,我可以将这个ENV变量设置为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
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