我想在Rails中“伪造”一个404页面。在PHP中,我只会发送一个带有错误代码的报头:
header("HTTP/1.0 404 Not Found");
Rails如何做到这一点?
我想在Rails中“伪造”一个404页面。在PHP中,我只会发送一个带有错误代码的报头:
header("HTTP/1.0 404 Not Found");
Rails如何做到这一点?
当前回答
不要自己渲染404页面,没有理由;Rails已经内置了这个功能。如果你想显示一个404页面,在ApplicationController中创建一个render_404方法(或者我称之为not_found),如下所示:
def not_found
raise ActionController::RoutingError.new('Not Found')
end
Rails还以同样的方式处理AbstractController::ActionNotFound和ActiveRecord::RecordNotFound。
这样做有两个好处:
1)它使用Rails内置的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)
和小型试验:
assert_raises(ActionController::RoutingError) do
get '/something/you/want/to/404'
end
OR从控制器动作中找不到的Rails渲染404中引用更多信息
其他回答
你也可以使用渲染文件:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
你可以选择是否使用该布局。
另一种选择是使用Exceptions来控制它:
raise ActiveRecord::RecordNotFound, "Record not found."
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
<%= render file: 'public/404', status: 404, formats: [:html] %>
只需将此添加到要呈现到404错误页面的页面,就完成了。
由Steven Soroka提交的新选答案很接近,但不完整。测试本身隐藏了这样一个事实:这并不是返回一个真正的404,而是返回一个状态为200的“success”。原来的答案更接近,但试图呈现的布局,好像没有发生失败。这可以解决所有问题:
render :text => 'Not Found', :status => '404'
下面是我的一个典型的测试集,使用RSpec和Shoulda匹配器,我希望返回404:
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
我检查了所有这些元素:赋值变量、响应代码、响应内容类型、模板渲染、布局渲染、flash消息。
我将跳过严格的html应用程序的内容类型检查……有时。毕竟,“怀疑论者会检查所有的抽屉”:)
http://dilbert.com/strips/comic/1998-01-20/
仅供参考:我不建议测试控制器中发生的事情,即“should_raise”。你关心的是输出。上面的测试允许我尝试各种解决方案,无论解决方案是否引发异常、特殊呈现等,测试都保持不变。
如果希望以不同的方式处理不同的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