Ruby-on-rails 如何重定向到 Rails 中的 404?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2385799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to redirect to a 404 in Rails?
提问by Yuval Karmi
I'd like to 'fake' a 404 page in Rails. In PHP, I would just send a header with the error code as such:
我想在 Rails 中“伪造”一个 404 页面。在 PHP 中,我只会发送一个带有错误代码的标头,如下所示:
header("HTTP/1.0 404 Not Found");
How is that done with Rails?
Rails 是如何做到的?
回答by Steven Soroka
Don't render 404 yourself, there's no reason to; Rails has this functionality built in already. If you want to show a 404 page, create a render_404method (or not_foundas I called it) in ApplicationControllerlike this:
不要自己渲染404,没有理由;Rails 已经内置了这个功能。如果要显示 404 页面,请创建一个render_404方法(或not_found我称之为),ApplicationController如下所示:
def not_found
raise ActionController::RoutingError.new('Not Found')
end
Rails also handles AbstractController::ActionNotFound, and ActiveRecord::RecordNotFoundthe same way.
Rails 也处理AbstractController::ActionNotFound,ActiveRecord::RecordNotFound以同样的方式。
This does two things better:
这可以更好地做两件事:
1) It uses Rails' built in rescue_fromhandler to render the 404 page, and
2) it interrupts the execution of your code, letting you do nice things like:
1) 它使用 Rails 的内置rescue_from处理程序来呈现 404 页面,并且 2) 它会中断代码的执行,让你做一些不错的事情,例如:
user = User.find_by_email(params[:email]) or not_found
user.do_something!
without having to write ugly conditional statements.
无需编写丑陋的条件语句。
As a bonus, it's also super easy to handle in tests. For example, in an rspec integration test:
作为奖励,它在测试中也非常容易处理。例如,在 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)
And minitest:
和最小:
assert_raises(ActionController::RoutingError) do
get '/something/you/want/to/404'
end
OR refer more info from Rails render 404 not found from a controller action
回答by Simone Carletti
HTTP 404 Status
HTTP 404 状态
To return a 404 header, just use the :statusoption for the render method.
要返回 404 标头,只需使用:status渲染方法的选项。
def action
# here the code
render :status => 404
end
If you want to render the standard 404 page you can extract the feature in a method.
如果你想渲染标准的 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
and call it in your action
并在你的行动中调用它
def action
# here the code
render_404
end
If you want the action to render the error page and stop, simply use a return statement.
如果您希望操作呈现错误页面并停止,只需使用 return 语句。
def action
render_404 and return if params[:something].blank?
# here the code that will never be executed
end
ActiveRecord and HTTP 404
ActiveRecord 和 HTTP 404
Also remember that Rails rescues some ActiveRecord errors, such as the ActiveRecord::RecordNotFounddisplaying the 404 error page.
还要记住,Rails 会挽救一些 ActiveRecord 错误,例如ActiveRecord::RecordNotFound显示 404 错误页面。
It means you don't need to rescue this action yourself
这意味着你不需要自己拯救这个动作
def show
user = User.find(params[:id])
end
User.findraises an ActiveRecord::RecordNotFoundwhen the user doesn't exist. This is a very powerful feature. Look at the following code
User.findActiveRecord::RecordNotFound当用户不存在时引发。这是一个非常强大的功能。看下面的代码
def show
user = User.find_by_email(params[:email]) or raise("not found")
#?...
end
You can simplify it by delegating to Rails the check. Simply use the bang version.
您可以通过将检查委托给 Rails 来简化它。只需使用 bang 版本。
def show
user = User.find_by_email!(params[:email])
#?...
end
回答by Jaime Bellmyer
The newly Selected answer submitted by Steven Soroka is close, but not complete. The test itself hides the fact that this is not returning a true 404 - it's returning a status of 200 - "success". The original answer was closer, but attempted to render the layout as if no failure had occurred. This fixes everything:
Steven Soroka 提交的新 Selected 答案很接近,但不完整。测试本身隐藏了这样一个事实,即这不会返回真正的 404 - 它返回的是 200 状态 - “成功”。原始答案更接近,但试图呈现布局,就好像没有发生故障一样。这解决了一切:
render :text => 'Not Found', :status => '404'
Here's a typical test set of mine for something I expect to return 404, using RSpec and Shoulda matchers:
这是我的一个典型测试集,我希望使用 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
This healthy paranoia allowed me to spot the content-type mismatch when everything else looked peachy :) I check for all these elements: assigned variables, response code, response content type, template rendered, layout rendered, flash messages.
这种健康的偏执让我在其他一切看起来都很好时发现内容类型不匹配:) 我检查所有这些元素:分配的变量、响应代码、响应内容类型、模板呈现、布局呈现、Flash 消息。
I'll skip the content type check on applications that are strictly html...sometimes. After all, "a skeptic checks ALL the drawers" :)
有时,我将跳过对严格为 html 的应用程序的内容类型检查。毕竟,“怀疑论者检查所有抽屉”:)
http://dilbert.com/strips/comic/1998-01-20/
http://dilbert.com/strips/comic/1998-01-20/
FYI: I don't recommend testing for things that are happening in the controller, ie "should_raise". What you care about is the output. My tests above allowed me to try various solutions, and the tests remain the same whether the solution is raising an exception, special rendering, etc.
仅供参考:我不建议测试控制器中发生的事情,即“should_raise”。你关心的是输出。我上面的测试允许我尝试各种解决方案,无论解决方案是否引发异常、特殊渲染等,测试都保持不变。
回答by Paulo Fidalgo
You could also use the render file:
您还可以使用渲染文件:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
Where you can choose to use the layout or not.
您可以选择是否使用布局。
Another option is to use the Exceptions to control it:
另一种选择是使用异常来控制它:
raise ActiveRecord::RecordNotFound, "Record not found."
回答by Augustin Riedinger
The selected answer doesn't work in Rails 3.1+ as the error handler was moved to a middleware (see github issue).
所选答案在 Rails 3.1+ 中不起作用,因为错误处理程序已移至中间件(请参阅github 问题)。
Here's the solution I found which I'm pretty happy with.
这是我找到的解决方案,我很满意。
In ApplicationController:
在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
and in application.rb:
并在application.rb:
config.after_initialize do |app|
app.routes.append{ match '*a', :to => 'application#not_found' } unless config.consider_all_requests_local
end
And in my resources (show, edit, update, delete):
在我的资源中(显示、编辑、更新、删除):
@resource = Resource.find(params[:id]) or not_found
This could certainly be improved, but at least, I have different views for not_found and internal_error without overriding core Rails functions.
这当然可以改进,但至少,在不覆盖核心 Rails 函数的情况下,我对 not_found 和 internal_error 有不同的看法。
回答by Caner ?akmak
these will help you...
这些会帮助你...
Application Controller
应用控制器
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
Errors controller
错误控制器
class ErrorsController < ApplicationController
def error_404
@not_found_path = params[:not_found]
end
end
views/errors/error_404.html.haml
意见/错误/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
回答by Ahmed Reza
<%= render file: 'public/404', status: 404, formats: [:html] %>
just add this to the page you want to render to the 404 error page and you are done.
只需将此添加到要呈现到 404 错误页面的页面中即可。
回答by emptywalls
I wanted to throw a 'normal' 404 for any logged in user that isn't an admin, so I ended up writing something like this in Rails 5:
我想为任何不是管理员的登录用户抛出一个“正常”的 404,所以我最终在 Rails 5 中写了这样的东西:
class AdminController < ApplicationController
before_action :blackhole_admin
private
def blackhole_admin
return if current_user.admin?
raise ActionController::RoutingError, 'Not Found'
rescue ActionController::RoutingError
render file: "#{Rails.root}/public/404", layout: false, status: :not_found
end
end
回答by Arkadiusz Mazur
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
回答by Marek P?íhoda
To test the error handling, you can do something like this:
要测试错误处理,您可以执行以下操作:
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

