Witam,
Od jakiegoś czasu uczę się railsów. Celem zaliczenia pewnego przedmiotu na studiach (jak i zdobycia sławy, pieniędzy oraz posady programisty rails ) postanowiłem stworzyć cms’a opartego na owej technologii. Wszystko jest już prawie na ukończeniu, wiec powoli zacząłem się zabierać za optymalizację kodu, dodawanie wodotrysków w jquery i za podstawowe zabezpieczenia. I tu właśnie dochodzimy do sedna sprawy…
Jeżeli umieszczę gdzieś moją aplikację, wtedy serwer będzie ładował środowisko production i jeśli wystąpi jakiś błąd, wyświetlona zostanie któraś ze stron (404, 500) z folderu public. Co jeśli chciałbym wyświetlić bardziej dynamiczne strony (np. wyświetlajace error message i najpopularniejsze produkty), albo poprostu przekierowywał użytkownika na stronę główną?
Googlując przez przez jakiś czas znalazłem 3 następujące rozwiązania…
- Pierwszym sposobem jest skorzystanie z metody rescue_from w application_controller’rze i stworzenie controller’a specjalnie do wyświetlania błędów…
[code]unless Rails.application.config.consider_all_requests_local
rescue_from Exception, :with => :render_error
rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found
rescue_from AbstractController::ActionNotFound, :with => :render_not_found
rescue_from ActionController::RoutingError, :with => :render_not_found
rescue_from ActionController::UnknownController, :with => :render_not_found
rescue_from ActionController::UnknownAction, :with => :render_not_found
end
.
.
.
private
def render_error exception
Rails.logger.error(exception)
redirect_to root_path
#albo wyświetli stronę z controllera errors
render :controller=>‘errors’, :action=>‘error_500’, :status=>500
end
def render_not_found exception
Rails.logger.error(exception)
redirect_to root_path
#albo wyświetli stronę z controllera errors
render :controller=>‘errors’, :action=>‘error_404’, :status=>404
end[/code]
… ale to rozwiązanie nie dawje żadnych oczekiwanych rezultatów.
(Gdzieś czytałem, że to bug i ma to być poprawione w Rails 3.1)
- Drugim sposobem było dodanie
match "*path" , :to => "errors#error_404"
do routes.rb. Ta linijka kodu działała jedynie, jeśli użytkownik wywołał scieżkę typu http://www.aaa.pl/asdasdasdas. Jeśli wywołany zostałby adres http://www.aaa.pl/websites/asdasdasdas (controller istnieje, ale akcja nie), wtedy załadowana zostałaby statyczna strona z folderu public. Próbowałem jeszcze akrobacji typu "*path/*act" , :to => "products#show", :id=>1
albo match ":controller(/*act)" , :to => "products#show"
, ale one też nie zadziałały…
- Trzecim rozwiązaniem było stworzenie pliku z odpowiednim kodem i wrzucenie go do initializers’ów:
# initializers/error_pages.rb
module ActionDispatch
class ShowExceptions
protected
def rescue_action_in_public(exception)
status = status_code(exception).to_s
template = ActionView::Base.new(["#{Rails.root}/app/views"])
if ["404"].include?(status)
file = "/errors/404.html.erb"
else
file = "/errors/500.html.erb"
end
body = template.render(:file => file)
render(status, body)
end
end
end
To rozwiązanie było najbardziej obiecujące, bo działało na wszystkie błędne adresy jakie testowałem, ale niestety nie wyświetlało layaut’u (dodanie deklaracji layout ‘user’ w controllerze nic nie dało). Co prawda mógłbym w tym momencie wrzucić kod z layaoutu do każdego z plików z błędem, ale co byłoby wtedy z konwencją DRY?
Wiem, że gdzieś pewnie popełniłem błąd. Jestem również pewien, że podczas pracy nad aplikacjami ktoś z Was zetknął się z tym problemem, liczę więc na Waszą pomoc i doświadczenie…
Pzdr.