Подтвердить что ты не робот

Rails 4: страницы пользовательских ошибок для 404, 500 и откуда отправляется сообщение об ошибке 500 по умолчанию?

В настоящее время в производстве я получаю этот текст:

500 Internal Server Error
If you are the administrator of this website, then please read this web application     
log file and/or the web server log file to find out what went wrong.

Нет html на этой странице ничего.

Где находится этот код? У меня нет публики /500.html или что-то в этом отношении.

На моих маршрутах я:

  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

Как всегда загружать мои пользовательские ошибки? На некоторых ошибках он просто бросает, что текст из двух строк идет откуда-то из ядра rails, я хочу, чтобы он каждый раз собирал мои пользовательские страницы с ошибками! как? ТНХ!

4b9b3361

Ответ 1

Ошибка, с которой вы столкнулись, выбрасывается из

https://github.com/rails/rails/blob/4-0-stable/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L18-L22

Это означает, что код, за исключением которого вы получаете спасение, сам бросает исключения. Вы можете проверить свои журналы для текста:

Error during failsafe response:

чтобы определить, из чего действительно происходят исключения, и, таким образом, решить вашу проблему.

Ответ 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).

-

Есть два способа обработки исключений:

  1. Переопределение маршрутов 404/500
  2. Вызов контроллера

Наш драгоценный камень был разработан вокруг нашего 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')

Ответ 4

Вот последнее и быстрое исправление для показа пользовательской страницы 404_error.

  • Добавьте ниже строки в development.rb или production.rb в соответствии с вашим env.

config.exceptions_app = self.routes

config.consider_all_requests_local = false

  • Удалить все rm public/{404,500,422}.html
  • Создайте 404.html.erb файл в статической папке вашего проекта rails. Вы можете добавить свой собственный html здесь (это будет использование вашего макета приложения, поэтому не беспокойтесь о содержании заголовка и нижнего колонтитула)