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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:21:46  来源:igfitidea点击:

How to redirect to a 404 in Rails?

ruby-on-railsrubyhttphttp-status-code-404

提问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

从控制器操作中找不到 Rails 渲染 404 中的更多信息

回答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