Notre site exception_handler
gem peut être utilisé pour les pages d'erreur personnalisées de Ruby on Rails.
Comment cela fonctionne
Toutes les exceptions de Ruby on Rails sont traitées avec config.exceptions_app
. Elle est attribuée dans le config/application.rb
o config/environments/*.rb
fichiers - il doit s'agir d'un rappel :
config.exceptions_app définit l'application d'exception invoquée par le middleware ShowException lorsqu'une exception se produit. La valeur par défaut est ActionDispatch::PublicExceptions.new(Rails.public_path).
Chaque fois que Ruby on Rails rencontre une erreur, il invoque la fonction ShowExceptions
intergiciel. Celui-ci appelle exception_app
et envoie l'ensemble du request
(y compris exception
) à la exceptions_app
:
exceptions_app
doit fournir une réponse . Dans le cas contraire, 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
El failsafe
est enregistré comme FAILSAFE_RESPONSE
au sommet de ShowExceptions
.
Pages d'erreurs personnalisées
Si vous souhaitez créer des pages d'erreur personnalisées, vous devez injecter votre propre callback dans le fichier config.exceptions_app
. Cela peut être fait dans l'application ou avec une gemme :
Remarquez comment le call
est utilisée - c'est ainsi qu'un callback fonctionne. Ruby on Rails ( env
) est invoqué lorsque la demande est reçue de l'Internet ; lorsqu'une exception est levée, env
est transmis à exceptions_app
.
La qualité de votre traitement des exceptions dépendra de la manière dont vous gérez env
. C'est important ; le référencement self.routes
fait no faire progresser l'environnement.
Le meilleur moyen est de gérer les exceptions avec un contrôleur séparé. Cela vous permet de traiter la requête comme s'il s'agissait d'une autre vue, en accordant l'accès à l'objet de l'exception. layout
et d'autres composants ( model
/ email
).
Il y a deux des moyens de traiter les exceptions :
- Remplacement de
404
/ 500
itinéraires
- Appeler un contrôleur
Notre joyau a été conçu autour de notre controller
- invoqué chaque fois qu'un exception
est relevé. Cela donne complet contrôle sur le processus d'exception, permettant Mise en page 100 % personnalisée . Il fonctionne à 100% sur Ruby on Rails 5.
Gérer les exceptions de Ruby on Rails
Si le joyau ne vous intéresse pas, laissez-moi vous expliquer le processus :
Toutes les exceptions de Ruby on Rails sont gérées par la fonction config.exceptions_app
callback. Celle-ci est assignée dans le config/application.rb
o config/environments/*.rb
fichiers - il doit s'agir d'un rappel :
config.exceptions_app définit l'application d'exception invoquée par le middleware ShowException lorsqu'une exception se produit. La valeur par défaut est ActionDispatch::PublicExceptions.new(Rails.public_path).
Chaque fois qu'une exception est levée par votre application, la fonction ShowExceptions
est invoqué. Cet intergiciel intègre l'exception dans le fichier request
et le transmet à la config.exceptions_app
le rappel.
Par défaut, config.exceptions_app
pointe vers les routes. C'est pourquoi Rails est livré avec 404.html
, 500.html
y 422.html
en el public
dossier.
Si vous voulez créer personnalisé vous devez remplacer les pages d'exception config.exceptions_app
callback - en transmettant la demande erronée à un gestionnaire approprié, qu'il s'agisse d'une controller
o route
:
[ intergiciel ]
Les deux façons de gérer efficacement ce problème sont soit d'envoyer les demandes erronées vers les routes, soit d'invoquer un contrôleur.
La méthode la plus simple - et la plus courante - consiste à transmettre la demande aux routes ; malheureusement, cette méthode ignore la demande et vous empêche de détailler correctement les exceptions.
La meilleure solution consiste à invoquer un contrôleur distinct. Cela vous permettra de transmettre l'intégralité de la requête, ce qui vous permettra de l'enregistrer, de l'envoyer par courrier électronique ou de faire un certain nombre d'autres choses.
Erreurs 400 / 500
Rails peut sólo répondre par des erreurs HTTP-valides .
Alors que l'application exception peut être différent, le code d'état renvoyé debe être soit 40x
o 50x
. Ceci est conforme à la spécification HTTP et à la description de l'opération. aquí .
Cela signifie que, quelle que soit la solution de traitement des exceptions que vous utilisez/construisez, Ruby on Rails besoins pour retourner soit 40x
o 50x
au navigateur.
En d'autres termes, les pages d'erreur personnalisées n'ont pas grand-chose à voir avec le système de gestion de l'information. type d'exception - plus comment vous attrapez et servez le réponse du navigateur .
Par défaut, Ruby on Rails le fait avec 404.html
, 422.html
y 500.html
dans le public
dossier. Si vous souhaitez gérer vous-même le flux d'exceptions, vous devez supprimer ces fichiers et acheminer les demandes erronées vers votre propre système de gestion des exceptions. exceptions_app
le rappel.
Cela peut être fait avec le routes
ou avec un controller
(que je vais expliquer maintenant) :
1. Routes
Le plus simple est de laisser les routes s'en charger.
Cette méthode est gonflée et nécessite l'utilisation de plusieurs actions. Il est également difficile de gérer les réponses.
Cela montre comment remplacer le exceptions_app
avec les routes directement :
# config/application.rb
config.exceptions_app = self.routes
Voici le code dont je dispose (Ruby 2.0.0 et Ruby on Rails 4.0) :
Configuration de l'application
#config/application.rb
config.exceptions_app = self.routes
Routes
#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
Erreurs Mise en page (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'erreurs
#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 des "routes" pour sa simplicité, elle n'est ni efficace ni modulaire. En effet, si votre application a un semblant d'orientation objet, vous la rejetterez rapidement comme un hack.
A beaucoup Le moyen le plus sûr est d'utiliser un contrôleur personnalisé pour attraper l'exception pure. De cette façon, vous pouvez construire le flux en fonction de la structure globale de votre application :
2. Contrôleur
L'autre option consiste à acheminer toutes les demandes vers un contrôleur.
C'est infiniment plus puissant car cela vous permet de prendre la requête (exception) et de la transmettre aux vues, tout en la gérant dans le backend. Cela permet par exemple de l'enregistrer dans la base de données.
Ce site Gist montre comment.
Cela signifie que nous pouvons nous accrocher à l'intergiciel et passer l'ensemble de la 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 voulez le faire manuellement, voici comment :
Configuration
La beauté de cette méthode est qu'elle s'accroche directement à config.exceptions_app
. Cela signifie que toute exception peut être traitée de manière native, ce qui permet une plus grande efficacité. Pour s'assurer que cela fonctionne, vous devez mettre le code suivant dans le fichier config/application.rb
( exceptions_app
ne fonctionne que dans production
- development
montre les erreurs) :
#config/application.rb
config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
Pour tester, vous pouvez mettre les requêtes "local" à false :
#config/environments/development.rb
config.consider_all_requests_local = false # true
Contrôleur
L'étape suivante consiste à ajouter un exception
contrôleur. Bien que cela puisse être géré dans application_controller
il est de loin préférable de l'extraire à part. Remarquez l'appel du 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 vues à ajouter pour que cela fonctionne.
Le premier est le exception/show
et le second est la vue layouts/error
. La première consiste à donner à l exception_contoller#show
une vue, et la seconde pour 500
erreurs internes du serveur.
#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>
Conclusion
El exception n'a pas autant d'importance que le code d'erreur .
Lorsque Ruby on Rails lève une exception, il attribue l'un des codes de réponse HTTP ci-dessus. Ces codes permettent à votre navigateur de déterminer si la requête a abouti.
Lorsque vous traitez des exceptions, vous devez vous assurer que vous êtes en mesure de gérer 40*
(qui utilisera généralement la même mise en page que le reste de votre application) et l'onglet 50*
(qui auront besoin de leur propre mise en page).
Dans les deux cas, il est préférable d'utiliser un système séparé de exception
qui vous permettra de gérer le exception
comme un objet.
0 votes
blog.grepruby.com/2015/04/pages-d'erreurs-personnalisées-avec-rails-4.html
2 votes
Cet article contient une explication concise de l'endroit où s'affiche cette page d'erreur Rails, sous la rubrique "inconvénients" : mattbrictson.com/dynamic-rails-error-pages . Ainsi, si la page d'erreur contient des erreurs, Rails le reconnaît et affiche l'erreur en texte clair que l'on voit ici. Ce n'est pas aussi détaillé que certains des exemples ci-dessous, mais cela peut être une solution rapide pour certaines personnes !
0 votes
Je suis également confronté au même problème. Rubytastic, pouvez-vous m'aider à résoudre ce problème ?