我想在Rails中“伪造”一个404页面。在PHP中,我只会发送一个带有错误代码的报头:

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

Rails如何做到这一点?


当前回答

<%= 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
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

由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页面,没有理由;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中引用更多信息

HTTP 404状态

要返回404报头,只需使用render方法的: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

用户。find在用户不存在时引发ActiveRecord::RecordNotFound。这是一个非常强大的功能。请看下面的代码

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

您可以通过将检查委托给Rails来简化它。简单地使用bang版本。

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