http-status-code-404 - rails如何重定向到了404?

  显示原文与译文双语对照的内容

我想在 Rails'伪'404页面。 在PHP中,我只发送一个带有错误代码的标头,如下所示:


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

Rails 怎么做的?

时间:

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


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

Rails 也处理 AbstractController::ActionNotFound,并且使用同样的方式。

这样做更好:

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)

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

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


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

你可以通过委托 Rails 来简化它。 只需使用blast版本。


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

由 Steven Soroka所提交的新答案是关闭的,但没有完成。 测试本身隐藏了这样一个事实:它没有返回一个真实的404 - 它返回的状态为 200 -"成功"。 原始答案较接近,但试图呈现布局,似乎没有出现故障。 这将修复一切:


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

下面是我期望返回 404的典型测试集,使用RSpec和应该匹配:


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

这个健康的偏执让我发现 Content-Type 不匹配时,所有其他的都是 peachy: ) 我检查所有这些元素: 分配的变量,响应代码,响应内容类型,呈现的模板,呈现的布局,Flash 消息。

我将跳过对严格html的应用程序的内容类型检查- 有时。 毕竟,"怀疑者检查所有的抽屉。":)

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

注:我不建议对控制器中发生的事情进行测试,IE"should_raise"。 你关心的是输出。 上面的测试允许我尝试各种解决方案,并且不管解决方案是否引发异常,特殊呈现等,测试仍然是一样的。

这些会帮助你。。

应用程序控制器


 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

位错误控制器


 class ErrorsController <ApplicationController
 def error_404
 @not_found_path = params[:not_found]
 end
 end

views/errors/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

你也可以使用渲染 file:


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

你可以选择是否使用布局。

另一个选项是使用异常来控制它:


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

选择的答案在 Rails 3.1 + 中不起作用,因为错误处理程序被移动到中间件( 查看 github问题 。htm ) 。

这是我发现的我非常满意的解决方案。

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视图,而不重写核心 Rails 函数。

要测试错误处理,你可以执行如下操作:


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

...