Actuellement en production je reçois ce texte:
500 Internal Server Error
If you are the administrator of this website, then please read this web application's
log file and/or the web server's log file to find out what went wrong.
Aucun html dans cette page rien.
Où se situe ce code? J'ai non public/500.html ou quoi que ce soit à cet égard.
Dans mes itinéraires j'ai:
get "/404", :to => "errors#error_404"
get "/422", :to => "errors#error_404"
get "/500", :to => "errors#error_500"
get "/505", :to => "errors#error_505"
ErrorsController:
class ErrorsController < ApplicationController
def sub_layout
"left"
end
def error_404
render :status => 404, :formats => [:html], :layout => "white", :sub_layout => "left"
end
def error_422
render :status => 422, :formats => [:html], :layout => "white", :sub_layout => "left"
end
def error_500
render :status => 500, :formats => [:html], :layout => "white", :sub_layout => "left"
end
def error_505
render :status => 505, :formats => [:html], :layout => "white", :sub_layout => "left"
end
end
Comment le faire charger mes erreurs personnalisées toujours? Sur certaines erreurs, il suffit de jeter ce texte de 2 lignes provenant quelque part de Rails core, je veux qu'il récupère mes pages d'erreur personnalisées à chaque fois! Comment? Thx!
L'erreur que vous rencontrez est renvoyée
Cela signifie que le code par lequel vos exceptions sont sauvées est lui-même une exception. Vous pouvez vérifier le texte de vos journaux:
Error during failsafe response:
identifier l'origine des exceptions et résoudre ainsi votre problème.
Notre gemme exception_handler
est maintenant considérée comme la plus populaire (pages d'erreur personnalisées Rails)
Toutes les Rails sont gérées avec config.exceptions_app
. Ceci est attribué dans les fichiers config/application.rb
ou config/environments/*.rb
- il doit s'agir d'un rappel:
Chaque fois que Rails rencontre une erreur, il appelle le ShowExceptions
middleware . Ceci appelle exception_app
Et envoie le request
entier (y compris exception
) au exceptions_app
:
exceptions_app
doit livrer une réponse . Sinon, le failsafe
est chargé:
# show_exceptions.rb#L38
def render_exception(env, exception)
wrapper = ExceptionWrapper.new(env, exception)
status = wrapper.status_code
env["action_dispatch.exception"] = wrapper.exception
env["PATH_INFO"] = "/#{status}"
response = @exceptions_app.call(request.env) # => exceptions_app callback
response[1]["X-Cascade"] == "pass" ? pass_response(status) : response
rescue Exception => failsafe_error # => raised if exceptions_app false
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
FAILSAFE_RESPONSE
end
Le failsafe
est stocké sous le nom FAILSAFE_RESPONSE
en haut de ShowExceptions
.
Si vous voulez créer des pages d'erreur personnalisées, vous devez injecter votre propre rappel dans config.exceptions_app
. Cela peut être fait en application ou avec une gemme:
Remarquez comment la méthode call
est utilisée - voici comment fonctionne un rappel. Rails (env
) est appelé lorsque la demande est reçue d'Internet; lorsqu'une exception est déclenchée, env
est passé à exceptions_app
.
La qualité de la gestion de vos exceptions dépendra de la façon dont vous gérez env
. C'est important; référencer self.routes
fait pas reporter l'environnement.
Le meilleur moyen est de gérer les exceptions avec un contrôleur séparé. Cela vous permet de traiter la demande comme s'il ne s'agissait que d'une autre vue, en accordant l'accès au layout
et à d'autres composants (model
/email
).
-
Il y a deux façons de gérer les exceptions:
404
/500
Notre bijou a été conçu autour de notre controller
invoqué chaque fois qu'un exception
est levé. Cela donne à complet le contrôle du processus d’exception, permettant ainsi une mise en page 100% personnalisée :
ExceptionHandler
est à présent le principal joyau de production de pages d'erreur personnalisées pour Rails.
Maintenu pendant plus de 3 ans, c'est le joyau d'exception le plus simple et le plus puissant pour Rails. Cela fonctionne à 100% sur Rails 5 et a déjà été téléchargé plus de 70 000 fois.
Le dernier ver (0.8.0.0
a les mises à jour suivantes:
Vous pouvez en lire plus ici .
Si vous n'êtes pas intéressé par la gemme, laissez-moi vous expliquer le processus:
Toutes les exceptions Rails sont gérées avec le rappel ) config.exceptions_app
. This est attribué dans les fichiers config/application.rb
ou config/environments/*.rb
- il doit s'agir d'un rappel:
Chaque fois qu'une exception est générée par votre application, le middleware ShowExceptions
est appelé. Ce middleware construit l'exception dans le request
et le transmet au rappel config.exceptions_app
.
Par défaut, config.exceptions_app
Pointe sur les itinéraires. C'est pourquoi Rails est fourni avec 404.html
, 500.html
Et 422.html
Dans le dossier public
.
Si vous voulez créer des pages d'exception custom, vous devez écraser le rappel config.exceptions_app
- en transmettant la demande erronée à un gestionnaire approprié, que ce soit controller
ou route
:
[[middleware]]
Les deux manières de gérer cela efficacement sont d’envoyer les requêtes erronées aux routes ou d’appeler un contrôleur.
Le moyen le plus simple - et le plus courant - consiste à transmettre la demande aux itinéraires; malheureusement, cela ignore la demande et vous empêche de détailler correctement les exceptions.
Le meilleur moyen consiste à appeler un contrôleur séparé. Cela vous permettra de passer la demande en entier, de la sauvegarder, de l'envoyer par courrier électronique ou de faire un certain nombre d'autres choses.
-
Rails peut seulement répondre avec des erreurs HTTP-valides .
Tandis que l'application exception peut être différente, le code d'état renvoyé devrait être 40x
Ou 50x
. Ceci est conforme aux spécifications HTTP et décrit ici ↴
Cela signifie que quelle que soit la solution de traitement des exceptions que vous utilisez/générez, Rails nécessite pour renvoyer 40x
Ou 50x
erreurs au navigateur.
En d'autres termes, les pages d'erreur personnalisées ont peu à voir avec le type d'exception - plus comment vous attrapez et servez la réponse du navigateur.
Par défaut, Rails le fait avec les fichiers 404.html
, 422.html
Et 500.html
Dans le dossier public
. Si vous le souhaitez pour gérer vous-même le flux des exceptions, vous devez supprimer ces fichiers et canaliser les demandes erronées vers votre propre rappel exceptions_app
.
Cela peut être fait avec le routes
ou avec un controller
(que je vais expliquer maintenant):
Le moyen le plus simple est de laisser les itinéraires le gérer.
Cette méthode est gonflée et nécessite l'utilisation de plusieurs actions. Il est également difficile de gérer les réponses.
Ce tutoriel explique:
Cela montre comment remplacer le exceptions_app
Par les routes directement:
# config/application.rb
config.exceptions_app = self.routes
Voici le code que j'ai (Ruby 2.0.0, Rails 4.0)]:
Configuration de l'application
#config/application.rb
config.exceptions_app = self.routes
Itinéraires
#config/routes.rb
if Rails.env.production?
get '404', to: 'application#page_not_found'
get '422', to: 'application#server_error'
get '500', to: 'application#server_error'
end
Contrôleur d'application
#controllers/application_controller.rb
def page_not_found
respond_to do |format|
format.html { render template: 'errors/not_found_error', layout: 'layouts/application', status: 404 }
format.all { render nothing: true, status: 404 }
end
end
def server_error
respond_to do |format|
format.html { render template: 'errors/internal_server_error', layout: 'layouts/error', status: 500 }
format.all { render nothing: true, status: 500}
end
end
Mise en page des erreurs (totalement statique - pour les erreurs de serveur uniquement)
#views/layouts/error.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= action_name.titleize %> :: <%= site_name %></title>
<%= csrf_meta_tags %>
<style>
body {
background: #fff;
font-family: Helvetica, Arial, Sans-Serif;
font-size: 14px;
}
.error_container {
display: block;
margin: auto;
margin: 10% auto 0 auto;
width: 40%;
}
.error_container .error {
display: block;
text-align: center;
}
.error_container .error img {
display: block;
margin: 0 auto 25px auto;
}
.error_container .message strong {
font-weight: bold;
color: #f00;
}
</style>
</head>
<body>
<div class="error_container">
<%= yield %>
</div>
</body>
</html>
Vues d'erreur
#views/errors/not_found_error.html.erb
<div class="error">
<h2>Sorry, this page has moved, or doesn't exist!</h2>
</div>
#views/errors/internal_server_error.html.erb
<div class="error">
<div class="message">
<strong>Error!</strong>
We're sorry, but our server is experiencing problems :(
</div>
</div>
Si beaucoup préfèrent la méthode "routes" pour sa simplicité, elle n’est ni efficace ni modulaire. En effet, si votre application présente un semblant d'orientation d'objet, vous la rejetterez rapidement comme un hack.
Une méthode beaucoup plus efficace consiste à utiliser un contrôleur personnalisé pour intercepter l’exception pure. De cette façon, vous pouvez construire le flux en fonction de la structure globale de votre application:
L'autre option consiste à acheminer toutes les demandes vers un contrôleur.
C'est infiniment plus puissant car il vous permet de prendre la requête (exception) et de la transmettre aux vues, tout en la gérant dans le backend. Cela permettra aux utilisateurs de l'enregistrer dans la base de données.
Ce Gist montre comment:
Cela signifie que nous pouvons nous connecter au middleware et transmettre l'ensemble demande à un contrôleur.
Si ce contrôleur est soutenu par un modèle et des vues, nous pouvons l'extraire dans une gemme (ce que nous avons fait). Si vous vouliez le faire manuellement, voici comment:
-
Config
La beauté de cette méthode est qu’elle s’accroche directement à config.exceptions_app
. Cela signifie que toute exception peut être gérée de manière native, permettant plus d'efficacité. Pour vous assurer que cela fonctionne, vous devez placer le code suivant dans config/application.rb
(exceptions_app
Ne fonctionne que dans production
- development
indique les erreurs):
#config/application.rb
config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
Pour tester, vous pouvez définir les requêtes "locales" sur false:
#config/environments/development.rb
config.consider_all_requests_local = false # true
-
Contrôleur
L'étape suivante consiste à ajouter un contrôleur exception
. Bien que cela puisse être géré dans application_controller
, Il est de loin préférable d'extraire dans son propre. Notez l’appel de application.rb
- ExceptionController.action(:show)
:
#app/controllers/exception_controller.rb
class ExceptionController < ApplicationController
#Response
respond_to :html, :xml, :json
#Dependencies
before_action :status
#Layout
layout :layout_status
####################
# Action #
####################
#Show
def show
respond_with status: @status
end
####################
# Dependencies #
####################
protected
#Info
def status
@exception = env['action_dispatch.exception']
@status = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code
@response = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name]
end
#Format
def details
@details ||= {}.tap do |h|
I18n.with_options scope: [:exception, :show, @response], exception_name: @exception.class.name, exception_message: @exception.message do |i18n|
h[:name] = i18n.t "#{@exception.class.name.underscore}.title", default: i18n.t(:title, default: @exception.class.name)
h[:message] = i18n.t "#{@exception.class.name.underscore}.description", default: i18n.t(:description, default: @exception.message)
end
end
end
helper_method :details
####################
# Layout #
####################
private
#Layout
def layout_status
@status.to_s == "404" ? "application" : "error"
end
end
-
Vues
Il y a deux points de vue à ajouter pour que cela fonctionne.
La première est la vue exception/show
Et la seconde est la layouts/error
. La première consiste à donner une vue au exception_contoller#show
, Et la seconde aux erreurs de serveur interne 500
.
#app/views/exception/show.html.erb
<h1><%= details[:name] %></h1>
<p><%= details[:message] %></p>
#app/views/layouts/error.html.erb (for 500 internal server errors)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Error</title>
<style>
html {
height: 100%;
background: #fff;
}
body {
font-family: Helvetica, Arial, Sans-Serif;
font-size: 14px;
}
.error_container {
display: block;
margin: auto;
margin: 10% auto 0 auto;
width: 40%;
}
.error_container .error {
display: block;
text-align: center;
}
.error_container .error img {
display: block;
margin: 0 auto 15px auto;
}
.error_container .message > * {
display: block;
}
.error_container .message strong {
font-weight: bold;
color: #f00;
}
</style>
</head>
<body>
<div class="error_container"><%= yield %></div>
</body>
</html>
Le exception importe moins que le code d'erreur.
Lorsque Rails déclenche une exception, il attribue l'un des codes de réponse HTTP
ci-dessus. Ils permettent à votre navigateur de déterminer si la demande a abouti.
Lorsque vous traitez avec des exceptions, vous devez vous assurer que vous êtes capable de gérer les erreurs 40*
(Qui utilisent généralement la même présentation que le reste de votre application) et les erreurs 50*
(Qui nécessiteront leur propre mise en page).
Dans les deux cas, vous ferez mieux d'utiliser un contrôleur exception
distinct, qui vous permettra de gérer le exception
en tant qu'objet.
Les pages d'erreur dans l'application doivent être aussi simples que possible. La même recommandation concerne leur rendu. Si votre application renvoie 500 codes de réponse HTTP, cela signifie que les choses se sont déjà mal passées. Et il est possible que vous ne puissiez pas afficher la page d'erreur et l'afficher à l'utilisateur.
Dans l'idéal, les pages d'erreur doivent être un code HTML simple servi directement par votre serveur Web, sans toucher le serveur d'applications.
En parlant de Rails implémentation de cette idée. Il est basé sur l’utilisation d’un pipeline d’actifs pour la précompilation des pages statiques HTML.
Premièrement, ajoutez un nouveau type d’actif (Rails> 4.1):
# config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(404.html 500.html)
Rails.application.config.assets.paths << Rails.root.join('app/assets/html')
Rails.application.config.assets.register_mime_type('text/html', '.html')
Si le moteur de gabarit utilise (par exemple, slim, haml), enregistrez-le via un initialiseur:
# for Slim
Rails.application.assets.register_engine('.slim', Slim::Template)
# for Haml
Rails.application.assets.register_engine('.haml', Tilt::HamlTemplate)
Vous êtes maintenant prêt à créer de jolies pages d'erreur dans le répertoire app/assets/html à l'aide de votre moteur de modèle favori et de Rails helpers de vue intégrés.
Le pipeline d’actifs de production ajoute un condensé aux actifs compilés et stocke les fichiers dans un dossier par défaut (généralement partagé/public/actifs sur le serveur de production). Vous pouvez utiliser capistrano pour copier des pages d'erreur à la racine du serveur Web:
# config/deploy.rb
# Capistrano 3 only
namespace :deploy do
desc 'Copy compiled error pages to public'
task :copy_error_pages do
on roles(:all) do
%w(404 500).each do |page|
page_glob = "#{current_path}/public/#{fetch(:assets_prefix)}/#{page}*.html"
# copy newest asset
asset_file = capture :Ruby, %Q{-e "print Dir.glob('#{page_glob}').max_by { |file| File.mtime(file) }"}
if asset_file
execute :cp, "#{asset_file} #{current_path}/public/#{page}.html"
else
error "Error #{page} asset does not exist"
end
end
end
end
after :finishing, :copy_error_pages
end
Et dernière chose. Indiquez au serveur Web d'utiliser ces fichiers pour certains codes d'erreur HTTP (exemple de configuration nginx):
error_page 500 502 503 504 /500.html;
error_page 404 /404.html;
Pour le pignon 3, vous avez besoin de quelque chose comme ceci (testé avec Rails 5)):
# config/environments/production.rb
config.assets.configure do |env|
env.register_transformer 'text/slim', 'text/html', Slim::Template
env.register_mime_type 'text/slim', extensions: ['.html']
env.register_engine '.slim', Slim::Template
end
# config/initializers/assets.rb
Rails.application.config.assets.precompile += %w(404.html 500.html)
Rails.application.config.assets.paths << Rails.root.join('app/assets/html')