답변:
404를 직접 렌더링하지 마십시오. 이유가 없습니다. Rails에는이 기능이 이미 내장되어 있습니다. 404 페이지를 표시하려면 다음 render_404
과 not_found
같이 메소드를 작성하십시오 (또는 호출 한대로) ApplicationController
.
def not_found
raise ActionController::RoutingError.new('Not Found')
end
또한 핸들 레일 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
또는 Rails render 404의 추가 정보를 컨트롤러 작업에서 찾을 수 없습니다.
ActionController::RecordNotFound
더 나은 옵션 인 것 같습니다 .
expect { visit '/something/you/want/to/404' }.to raise_error(ActionController::RoutingError)
를 통해 /를 stackoverflow.com/a/1722839/993890
404 헤더를 반환하려면 :status
render 메소드 의 옵션을 사용하십시오 .
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
또한 Rails ActiveRecord::RecordNotFound
는 404 오류 페이지 표시 와 같은 일부 ActiveRecord 오류를 복구합니다 .
그것은 당신이이 행동을 스스로 구출 할 필요가 없다는 것을 의미합니다
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에 위임하여 단순화 할 수 있습니다. 뱅 버전을 사용하십시오.
def show
user = User.find_by_email!(params[:email])
# ...
end
Steven Soroka가 제출 한 새로 선택된 답변은 가깝지만 완료되지 않았습니다. 테스트 자체는 이것이 실제 404를 반환하지 않는다는 사실을 숨 깁니다. 200- "성공"상태를 반환합니다. 원래의 대답은 더 가까웠지만 실패가없는 것처럼 레이아웃을 렌더링하려고 시도했습니다. 이것은 모든 것을 고친다 :
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
이 건전한 편집증은 다른 모든 것이 복숭아처럼 보일 때 콘텐츠 유형 불일치를 발견 할 수있게했습니다. :) 할당 된 변수, 응답 코드, 응답 콘텐츠 유형, 템플릿 렌더링, 레이아웃 렌더링, 플래시 메시지 등 모든 요소를 확인합니다.
html ... 언제나 엄격하게 응용 프로그램의 내용 유형 검사를 건너 뛸 것입니다. 결국, "회의론자는 모든 서랍을 확인합니다":)
http://dilbert.com/strips/comic/1998-01-20/
참고 : 컨트롤러에서 발생하는 일 (예 : "should_raise")에 대한 테스트는 권장하지 않습니다. 관심있는 것은 출력입니다. 위의 테스트를 통해 다양한 솔루션을 시도 할 수 있었고 솔루션이 예외, 특수 렌더링 등을 발생시키는 지 여부와 상관없이 테스트는 동일하게 유지됩니다.
render :text => 'Not Found', :status => :not_found
입니다.
config.consider_all_requests_local
에서 매개 변수가 true로 설정되어 있는 개발중인 디버그 화면을 렌더링 할 때 200을 반환한다는 것 environments/development.rb
입니다. 승인 된 솔루션에 설명 된대로 준비 / 생산에서 오류가 발생하면 200이 아니라 404를 얻게됩니다.
오류 처리기가 미들웨어로 이동함에 따라 선택한 답변이 Rails 3.1 이상에서 작동하지 않습니다 ( github issue 참조 ).
내가 찾은 해결책은 다음과 같습니다.
에서 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
이것은 확실히 개선 될 수 있지만 적어도 핵심 Rails 함수를 재정의하지 않고 not_found 및 internal_error에 대한 다른 견해를 가지고 있습니다.
|| not_found
부분 이 필요하지 않고 전화 find!
(뱅뱅을 통지)하면 리소스를 검색 할 수 없을 때 ActiveRecord :: RecordNotFound가 발생합니다. 또한 if 조건에서 ActiveRecord :: RecordNotFound를 배열에 추가하십시오.
StandardError
하지 않을 것이다 Exception
. 사실은 내가 표준 500 정적 페이지를 떠나 사용자 정의를 사용하지 것이다 render_500
의미 모두에서 거 야 명시 적으로 rescue_from
404 관련된 오류의 배열
이것들이 당신을 도울 것입니다 ...
어플리케이션 컨트롤러
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
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
관리자가 아닌 로그인 한 사용자에게 '일반'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
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
오류 처리를 테스트하기 위해 다음과 같이 할 수 있습니다.
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 / B 테스트 등을 수행하는 등의 작업을 수행 할 수 있습니다.
여기서는 기본 로직을 ApplicationController에 배치했지만 더 구체적인 컨트롤러에 배치하여 하나의 컨트롤러에 대해서만 특수 로직을 가질 수 있습니다.
ENV [ 'RESCUE_404']와 함께 if를 사용하는 이유는 AR :: RecordNotFound의 발생을 개별적으로 테스트 할 수 있기 때문입니다. 테스트 에서이 ENV var를 false로 설정할 수 있으며 rescue_from이 실행되지 않습니다. 이 방법으로 조건부 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