Rails'te 404 sayfayı 'taklit etmek' istiyorum. PHP, ben sadece böyle hata kodu ile bir başlık göndermek istiyorum:
header("HTTP/1.0 404 Not Found");
Rails ile bu nasıl yapılır?
Rails'te 404 sayfayı 'taklit etmek' istiyorum. PHP, ben sadece böyle hata kodu ile bir başlık göndermek istiyorum:
header("HTTP/1.0 404 Not Found");
Rails ile bu nasıl yapılır?
Yanıtlar:
404'ü kendiniz yaratmayın, bunun için bir sebep yok; Rails zaten bu işlevselliğe sahiptir. Bir 404 sayfası göstermek istiyorsanız, render_404
(veya not_found
benim dediğim ApplicationController
gibi ) bir yöntem oluşturun :
def not_found
raise ActionController::RoutingError.new('Not Found')
end
Ayrıca kolları Raylar AbstractController::ActionNotFound
ve ActiveRecord::RecordNotFound
aynı şekilde.
Bu iki şeyi daha iyi yapar:
1) rescue_from
404 sayfasını oluşturmak için Rails yerleşik işleyicisini kullanır ve 2) kodunuzun yürütülmesini kesintiye uğratır ve aşağıdaki gibi güzel şeyler yapmanıza izin verir:
user = User.find_by_email(params[:email]) or not_found
user.do_something!
çirkin koşullu ifadeler yazmak zorunda kalmadan.
Bir bonus olarak, testlerde de kullanımı çok kolaydır. Örneğin, bir rspec entegrasyon testinde:
# 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)
Ve en küçüğü:
assert_raises(ActionController::RoutingError) do
get '/something/you/want/to/404'
end
VEYA daha fazla bilgi için bkz. Rails render 404 bir denetleyici eyleminden bulunamadı
ActionController::RecordNotFound
Daha iyi bir seçenek gibi görünüyor ?
expect { visit '/something/you/want/to/404' }.to raise_error(ActionController::RoutingError)
/ via stackoverflow.com/a/1722839/993890
Bir 404 üstbilgisi döndürmek için, yalnızca :status
render yöntemi seçeneğini kullanın .
def action
# here the code
render :status => 404
end
Standart 404 sayfasını oluşturmak istiyorsanız, özelliği bir yöntemle ayıklayabilirsiniz.
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
ve onu eylemin içinde çağır
def action
# here the code
render_404
end
Eylemin hata sayfasını oluşturmasını ve durmasını istiyorsanız, bir return ifadesi kullanın.
def action
render_404 and return if params[:something].blank?
# here the code that will never be executed
end
Ayrıca Rails'in ActiveRecord::RecordNotFound
404 hata sayfasını görüntüleme gibi bazı ActiveRecord hatalarını kurtardığını unutmayın .
Bu, bu eylemi kendiniz kurtarmanıza gerek olmadığı anlamına gelir
def show
user = User.find(params[:id])
end
User.find
ActiveRecord::RecordNotFound
kullanıcı yokken bir yükseltir . Bu çok güçlü bir özellik. Aşağıdaki koda bakın
def show
user = User.find_by_email(params[:email]) or raise("not found")
# ...
end
Onaylama işlemini Rails'e atayarak basitleştirebilirsiniz. Sadece patlama sürümünü kullanın.
def show
user = User.find_by_email!(params[:email])
# ...
end
Steven Soroka tarafından sunulan yeni Seçilen cevap yakın, ancak tam değil. Testin kendisi, bunun gerçek bir 404 döndürmemesi gerçeğini gizler - 200 değerini döndürüyor - "başarı". Orijinal yanıt daha yakındı, ancak hiçbir hata oluşmamış gibi düzeni oluşturmaya çalıştı. Bu her şeyi düzeltir:
render :text => 'Not Found', :status => '404'
RSpec ve Shoulda eşleştiricileri kullanarak 404'e dönmeyi umduğum bir şey için tipik bir test setim:
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
Bu sağlıklı paranoya, her şey şeftali gibi geldiğinde içerik türü uyuşmazlığını tespit etmeme izin verdi :) Tüm bu öğeleri kontrol ediyorum: atanan değişkenler, yanıt kodu, yanıt içerik türü, oluşturulan şablon, oluşturulan düzen, flaş mesajlar.
Kesinlikle html olan uygulamalarda içerik türü denetimini atlayacağım ... bazen. Sonuçta, "şüpheci TÜM çekmeceleri denetler" :)
http://dilbert.com/strips/comic/1998-01-20/
FYI: Denetleyicide olup bitenlerin test edilmesini önermiyorum, yani "gerekir". Önem verdiğiniz şey çıktı. Yukarıdaki testlerim çeşitli çözümleri denememe izin verdi ve çözümün bir istisna, özel oluşturma vb.
render :text => 'Not Found', :status => :not_found
.
config.consider_all_requests_local
true değerine ayarlanmış olduğunuz hata ayıklama ekranını oluştururken 200 değerini döndürmesidir environments/development.rb
. Kabul edilen çözümde açıklandığı gibi, bir aşama / üretimde bir hata ortaya çıkarsanız, kesinlikle 200 değil, 404 alırsınız.
Oluşturma dosyasını da kullanabilirsiniz:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
Düzeni kullanmayı ya da kullanmamayı seçebilirsiniz.
Başka bir seçenek, İstisnaları kontrol etmek için kullanmaktır:
raise ActiveRecord::RecordNotFound, "Record not found."
Hata işleyicisi bir ara katman yazılımına taşındığından seçilen yanıt Rails 3.1+ ile çalışmaz ( github sorununa bakın ).
İşte bulduğum çözüm oldukça mutlu.
İçinde 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
ve içinde application.rb
:
config.after_initialize do |app|
app.routes.append{ match '*a', :to => 'application#not_found' } unless config.consider_all_requests_local
end
Ve kaynaklarımda (göster, düzenle, güncelle, sil):
@resource = Resource.find(params[:id]) or not_found
Bu kesinlikle geliştirilebilir, ancak en azından çekirdek Rails işlevlerini geçersiz kılmadan not_found ve internal_error için farklı görünümlerim var.
|| not_found
parçaya ihtiyacınız yoktur , sadece arayın find!
(patlama dikkat edin) ve kaynak alınamadığı zaman ActiveRecord :: RecordNotFound atar. Ayrıca, if durumunda diziye ActiveRecord :: RecordNotFound ekleyin.
StandardError
ve kurtarmam Exception
. Aslında standart 500 statik sayfa bırakacağım ve özel kullanmıyorum render_500
, yani rescue_from
404
bunlar size yardımcı olacak ...
Uygulama Denetleyicisi
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
Hata denetleyicisi
class ErrorsController < ApplicationController
def error_404
@not_found_path = params[:not_found]
end
end
görünümler / hatalar / 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
<%= render file: 'public/404', status: 404, formats: [:html] %>
bunu 404 hata sayfasına oluşturmak istediğiniz sayfaya ekleyin ve işiniz bitti.
Yönetici olmayan herhangi bir giriş yapmış kullanıcı için 'normal' 404 atmak istedim, bu yüzden Rails 5'de böyle bir şey yazdım:
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
Hata işlemeyi test etmek için aşağıdakine benzer bir şey yapabilirsiniz:
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
Farklı 404'leri farklı şekillerde ele almak istiyorsanız, bunları kontrol cihazlarınızda yakalamayı düşünün. Bu, farklı kullanıcı grupları tarafından oluşturulan 404'lerin sayısını izleme, kullanıcılarla neyin yanlış gittiğini / kullanıcı deneyiminin hangi bölümünün ayarlanması gerektiğini, A / B testi vb.
Burada temel mantığı ApplicationController'a yerleştirdim, ancak yalnızca bir denetleyici için özel mantığa sahip olmak için daha spesifik denetleyicilere de yerleştirilebilir.
ENV ['RESCUE_404'] ile bir if kullanmamın sebebi, AR :: RecordNotFound'un yükseltilmesini izole olarak test edebilmemdir. Testlerde, bu ENV değişkenini false olarak ayarlayabilirim ve rescue_from'um ateşlemez. Bu şekilde yetiştirmeyi şartlı 404 mantığından ayrı olarak test edebilirim.
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