Нет html на этой странице ничего.
Где находится этот код? У меня нет публики /500.html или что-то в этом отношении.
Как всегда загружать мои пользовательские ошибки? На некоторых ошибках он просто бросает, что текст из двух строк идет откуда-то из ядра rails, я хочу, чтобы он каждый раз собирал мои пользовательские страницы с ошибками! как? ТНХ!
Ответ 2
📆 Обновление 2018 📆
Наш гем exception_handler
теперь считается самым популярным (пользовательские страницы ошибок Rails) ↴
Как это работает
Все исключения Rails обрабатываются с помощью config.exceptions_app
. Это назначается в файлах config/application.rb
или config/environments/*.rb
- это должен быть обратный вызов:
config.exceptions_app устанавливает приложение исключений, которое вызывается промежуточным программным обеспечением ShowException при возникновении исключения. По умолчанию используется ActionDispatch :: PublicExceptions.new(Rails.public_path).
Всякий раз, когда Rails обнаруживает ошибку, он вызывает промежуточное программное обеспечение ShowExceptions
. Это вызывает exception_app
и отправляет весь request
(включая exception
) на exceptions_app
:
![Middleware-Powered Exceptions]()
exceptions_app
необходимо доставить ответ. Если нет, то загружается failsafe
:
# 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
failsafe
сохраняется как FAILSAFE_RESPONSE
в верхней части ShowExceptions
.
Пользовательские страницы ошибок
Если вы хотите создать пользовательские страницы ошибок, вам нужно добавить свой собственный обратный вызов в config.exceptions_app
. Это может быть сделано в приложении или с помощью самоцвета:
![enter image description here]()
Обратите внимание, как используется метод call
- так работает обратный вызов. Rails (env
) вызывается при получении запроса из Интернета; когда возникает исключение, env
передается в exceptions_app
.
Качество вашей обработки исключений будет зависеть от того, как вы управляете env
. Это важно; ссылка self.routes
не переносит среду вперед.
Лучший способ - обрабатывать исключения с помощью отдельного контроллера. Это позволяет обрабатывать запрос, как если бы он был просто другим представлением, предоставляя доступ к layout
и другим компонентам (model
/email
).
-
Есть два способа обработки исключений:
- Переопределение маршрутов
404
/500
-
Вызов контроллера
Наш драгоценный камень был разработан вокруг нашего controller
- вызывается каждый раз, когда поднимается exception
. Это дает полный контроль над процессом исключения, позволяя использовать 100% фирменный макет:
![enter image description here]()
ExceptionHandler
теперь является ведущим производственным гемом пользовательских страниц ошибок для Rails.
Поддерживается в течение более 3 лет, это самый простой и мощный камень исключений для Rails. Он работает на Rails 5 на 100% и уже был загружен более 70000 раз.
Gem
Последняя версия 0.8.0.0
имеет следующие обновления:
- Пользовательские исключения
- Исключение "отображение" (выберите, какие исключения обрабатывать)
- Уведомления по электронной почте
- Бэкэнд модели
- Интеграция звездочек 4+
- RSpec Test Suite
- Представления на основе локали
Вы можете прочитать больше здесь.
Управление исключениями Rails
Если вы не заинтересованы в драгоценном камне, позвольте мне объяснить процесс:
Все исключения Rails обрабатываются обратным вызовом config.exceptions_app
. Это назначается в файлах config/application.rb
или config/environments/*.rb
- это должен быть обратный вызов:
config.exceptions_app устанавливает приложение исключений, которое вызывается промежуточным программным обеспечением ShowException при возникновении исключения. По умолчанию используется ActionDispatch :: PublicExceptions.new(Rails.public_path).
Когда ваше приложение вызывает исключение, вызывается промежуточное программное обеспечение ShowExceptions
. Это промежуточное ПО встраивает исключение в request
и перенаправляет его в обратный вызов config.exceptions_app
.
По умолчанию config.exceptions_app
указывает на маршруты. Вот почему Rails поставляется с 404.html
, 500.html
и 422.html
в папке public
.
Если вы хотите создать пользовательские страницы исключений, вам нужно переопределить обратный вызов config.exceptions_app
- передать ошибочный запрос соответствующему обработчику, будь то controller
или route
:
[[middleware]]
Два способа эффективно управлять этим - либо посылать ошибочные запросы на маршруты, либо вызывать контроллер.
Самый простой - и самый распространенный - способ перенаправить запрос на маршруты; к сожалению, это игнорирует запрос и мешает вам правильно детализировать исключения.
Лучший способ - вызвать отдельный контроллер. Это позволит вам передать весь запрос, чтобы сохранить его, отправить его по электронной почте или выполнить ряд других действий.
-
400/500 ошибок
Rails может отвечать только с ошибками, действительными по HTTP.
Хотя исключение приложения может отличаться, возвращаемый код состояния должен быть 40x
или 50x
. Это соответствует спецификации HTTP и обозначено здесь here.
Это означает, что независимо от того, какое решение для обработки исключений вы используете/строите, Rails должен возвращать в браузер ошибки 40x
или 50x
.
Другими словами, пользовательские страницы ошибок не имеют ничего общего с типом исключения - больше о том, как вы ловите и обслуживаете ответ браузера.
По умолчанию Rails делает это с файлами 404.html
, 422.html
и 500.html
в папке public
. Если вы хотите обрабатывать поток исключений самостоятельно, вам необходимо удалить эти файлы и направить ошибочные запросы на ваш собственный обратный вызов exceptions_app
.
Это можно сделать с помощью routes
или controller
(что я сейчас объясню):
1. Маршруты
Самый простой способ - позволить маршрутам справиться с этим.
Этот метод раздут и требует использования нескольких действий. Также сложно управлять ответами.
Здесь показано, как заменить exceptions_app
на маршруты напрямую:
# config/application.rb
config.exceptions_app = self.routes
Вот мой код (Ruby 2.0.0, Rails 4.0):
Конфигурация приложения
#config/application.rb
config.exceptions_app = self.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
Контроллер приложений
#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
Схема ошибок (полностью статическая - только для ошибок сервера)
#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>
Просмотр ошибок
#views/errors/not_found_error.html.erb
<div class="error">
<h2>Sorry, this page has moved, or does not 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>
Хотя многие предпочитают метод "маршрутов" из-за его простоты, он не является ни эффективным, ни модульным. Действительно, если ваше приложение имеет какое-либо подобие объектной ориентации, вы быстро отклоните его как хакерский.
Гораздо более убедительный способ - использовать собственный контроллер, чтобы поймать чистое исключение. Таким образом, вы можете построить поток в соответствии с общей структурой вашего приложения:
2. Контроллер
Другой вариант - направить все запросы к контроллеру.
Это намного мощнее, так как позволяет вам принимать запрос (исключение) и передавать его представлениям, одновременно управляя им в бэкэнде. Это позволит сохранить его в базе данных.
Эта суть показывает, как.
Это означает, что мы можем подключиться к промежуточному программному обеспечению & передать весь запрос контроллеру.
Если этот контроллер поддерживается моделью и представлениями, мы можем извлечь его в гем (что мы и сделали). Если вы хотите сделать это вручную, вот как:
-
Config
Прелесть этого метода в том, что он подключается непосредственно к config.exceptions_app
. Это означает, что любое исключение может быть обработано изначально, что обеспечивает большую эффективность. Чтобы убедиться, что это работает, вам нужно поместить следующий код в config/application.rb
(exceptions_app
работает только в production
- development
показывает ошибки):
#config/application.rb
config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
Для проверки вы можете установить для "локальных" запросов значение false:
#config/environments/development.rb
config.consider_all_requests_local = false # true
-
Контроллер
Следующим шагом является добавление контроллера exception
. Хотя это может быть обработано в application_controller
, гораздо лучше извлечь его самостоятельно. Обратите внимание на вызов от 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
-
Просмотров
Чтобы это работало, нужно добавить два представления.
Первый - это вид exception/show
, а второй - layouts/error
. Первый - дать представление о exception_contoller#show
, а второй - о внутренних ошибках сервера 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>
Заключение
Исключение не так важно, как код ошибки.
Когда Rails вызывает исключение, он присваивает один из приведенных выше кодов ответа HTTP
. Это позволяет вашему браузеру определить, был ли запрос успешным.
При работе с исключениями необходимо убедиться, что вы способны обрабатывать ошибки 40*
(которые обычно используют тот же макет, что и остальная часть вашего приложения), а также ошибки 50*
(для которых потребуется собственный макет).
В обоих случаях лучше всего использовать отдельный контроллер exception
, который позволит вам управлять exception
как объектом.
Ответ 3
Страницы ошибок в приложении должны быть максимально простыми. Та же рекомендация касается их рендеринга. Если ваше приложение возвращает 500 HTTP-ответов, это означает, что все уже не так. И есть вероятность, что вы не смогли отобразить страницу с ошибкой и отобразить ее пользователю.
В идеале страницы ошибок должны быть простым HTML, который подается непосредственно вашим веб-сервером без попадания на сервер приложений.
Говоря о реализации Rails этой идеи. Он основан на использовании конвейера ресурсов для предварительной компиляции статических страниц HTML.
Сначала добавьте новый тип активов (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')
Если используется шаблонный движок (например, slim, haml), зарегистрируйте его через инициализатор:
# for Slim
Rails.application.assets.register_engine('.slim', Slim::Template)
# for Haml
Rails.application.assets.register_engine('.haml', Tilt::HamlTemplate)
Теперь вы готовы создавать красивые страницы ошибок в каталоге app/assets/html, используя
ваш любимый механизм шаблонов и встроенные помощники Rails.
Советы для производства
В конвейере производственных активов добавляется дайджест к скомпилированным активам и хранится в папке по умолчанию
(как правило, общие/общедоступные/активы на производственном сервере).
Вы можете использовать capistrano для копирования страниц ошибок в корень веб-сервера:
# 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
И последнее. Скажите веб-серверу использовать эти файлы для определенных кодов ошибок HTTP
(пример конфигурации nginx):
error_page 500 502 503 504 /500.html;
error_page 404 /404.html;
Обновление звездочки
Для Sprocket 3 вам нужно что-то вроде этого (проверено с 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')