如何在Rails中redirect到404?

我想在Rails中“伪造”一个404页面。 在PHP中,我只需要发送一个包含错误代码的头文件:

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

Rails是如何完成的?

不要自己渲染404,没有理由; Rails已经内置了这个function。 如果你想显示一个404页面,在ApplicationController像这样创build一个render_404方法(或者我称之为not_found ):

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

Rails也以同样的方式处理AbstractController::ActionNotFoundActiveRecord::RecordNotFound

这样做有两个好处:

1)它使用Rails内置的rescue_from处理程序来呈现404页面,并且2)它中断代码的执行,让你做好事情,如:

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

而不必写丑陋的条件语句。

作为奖励,在testing中处理也非常容易。 例如,在rspec集成testing中:

 # 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 

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错误,比如显示404错误页面的ActiveRecord::RecordNotFound

这意味着你不需要自己拯救这个行动

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

当用户不存在时, User.find引发一个ActiveRecord::RecordNotFound 。 这是一个非常强大的function。 看下面的代码

 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 

Steven Soroka提交的新select的答案接近,但并不完整。 testing本身隐藏了这样一个事实:这不是返回一个真正的404 – 它返回一个200的状态 – “成功”。 原来的答案更接近,但试图渲染布局,就好像没有发生故障。 这解决了一切:

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

下面是我使用RSpec和Shoulda匹配器返回404的典型testing集:

 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 

这种健康的偏执让我发现内容types不匹配时,一切都看起来桃色:)我检查所有这些元素:分配的variables,响应代码,响应内容types,模板呈现,布局呈现,闪光消息。

我会跳过严格的HTML应用程序的内容types检查…有时。 毕竟,“怀疑论者检查所有的抽屉”:)

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

仅供参考:我不推荐testing控制器中正在发生的事情,即“should_raise”。 你关心的是输出。 我上面的testing允许我尝试各种解决scheme,testing保持不变,无论解决scheme是否引发exception,特殊渲染等

所选的答案在Rails 3.1+中不起作用,因为error handling程序被移到了中间件(参见github问题 )。

这是我find的解决scheme,我很满意。

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 

这当然可以改善,但至less,我有not_found和internal_error不重写核心Rails函数不同的意见。

你也可以使用渲染文件:

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

哪里可以select使用布局。

另一种select是使用例外来控制它:

 raise ActiveRecord::RecordNotFound, "Record not found." 

这些将帮助你…

应用控制器

 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 

为了testingerror handling,你可以这样做:

 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 

如果你想以不同的方式处理不同的404,可以考虑在你的控制器中捕捉它们。 这将允许您执行诸如跟踪由不同用户组生成的404数量的支持,支持与用户进行交互以找出错误的内容/用户体验的哪些部分可能需要调整,进行A / Btesting等。

我在这里将基础逻辑放在ApplicationController中,但是它也可以放在更具体的控制器中,只有一个控制器才有特殊的逻辑。

我使用ENV ['RESCUE_404']的原因是,我可以单独testingAR :: RecordNotFound的提升。 在testing中,我可以将这个ENV var设置为false,并且我的rescue_from不会触发。 这样我就可以testing独立于条件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 
 <%= render file: 'public/404', status: 404, formats: [:html] %> 

只需将此添加到您要呈现到404错误页面的页面即可。