Что лучшая стратегия состоит в том, чтобы обработать исключения и ошибки в направляющих?

Я задавался вопросом, совместно используют ли люди свои лучшие практики / стратегии относительно обрабатывания исключений и ошибок. Теперь я не спрашиваю, когда выдать исключение (это был throroughly, которому отвечают здесь: ТАК: Когда выдать Исключение). И я не использую это для своего потока приложения - но существуют законные исключения, которые происходят все время. Например, самым популярным был бы ActiveRecord:: RecordNotFound. Каков был бы лучший способ обработать его? DRY путь?

Прямо сейчас я делаю большую проверку в моем контроллере, итак, если Post.find(5) Ноль возвратов - я проверяю на это и бросаю сообщение "молния". Однако, в то время как это очень детализировано - это является немного громоздким в некотором смысле, что я должен проверить на исключения как этот в каждом контроллере, в то время как большинство из них является по существу тем же и имеет отношение к записи, не найденной или связанным записям, не найденным - такой как также Post.find(5) не найденный или при попытке отобразить комментарии, связанные с сообщением, которое не существует, который выдал бы исключение (что-то как Post.find(5).comments[0].created_at)

Я знаю, что можно сделать что-то вроде этого в ApplicationController и перезаписать его позже в конкретном контроллере/методе для получения большей детализированной поддержки, однако который был бы надлежащим способом сделать это?

class ApplicationController < ActionController::Base
    rescue_from ActiveRecord::RecordInvalid do |exception|
        render :action => (exception.record.new_record? ? :new : :edit)
    end
end

Также это работало бы в случае, если Post.find(5) не найденный, но что относительно Post.find(5).comments[0].created_at - Я подразумевал, что не могу выдать полноценное исключение, если сообщение существует, но не имеет комментариев, правильно?

Для суммирования до сих пор, я делал большую ручную проверку с помощью if/else/unless или случай/когда (и я признаюсь, иногда начинают/спасают), и проверяющий на ноль? или пустой?, и т.д., но там получен, чтобы быть лучшим способом, которым это кажется.

ОТВЕТЫ:

@Milan: Привет Милан спасибо за ответ - я соглашаюсь с тем, что Вы сказали, и я думаю, что неправильно использовал исключение слова. То, что я имел в виду, - то, что прямо сейчас я делаю много вещей как:

if Post.exists?(params[:post_id])
    @p = Post.find(params[:post_id])
else
    flash[:error] = " Can't find Blog Post"
end

И я делаю много этого вида "обработки исключений", я стараюсь не использовать, начинают/спасают. Но мне кажется, что это - достаточно общий результат/проверка/ситуация, что должна быть СУШИЛКА способ сделать это, не так ли? Как Вы сделали бы этот вид проверки?

Также, как обработал бы его в этом случае? Скажем, Вы хотите отобразиться, комментарий создал дату в Вашем представлении:

Last comment for this post at : <%= @post.comments[0].created_at %>

И это сообщение не имеет никаких комментариев. Можно сделать

Last comment for this post at : <%= @post.comments.last.created_at unless @post.comments.empty? %>

Вы могли сделать регистрацию контроллера. И т.д. Существует несколько способов сделать это. Но что "лучший" путь состоит в том, чтобы обработать это?

25
задан Community 23 May 2017 в 11:45
поделиться

2 ответа

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

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

Ваш второй пример (Post.find(5).comments[0].created_at) также не является исключительным. Некоторые посты просто не имеют комментариев, и вы знаете об этом заранее. Так почему это должно вызывать исключение?

То же самое происходит и с примером ActiveRecord::RecordInvalid. Просто нет причин обрабатывать этот случай с помощью исключения. То, что пользователь вводит в форму некорректные данные, является вполне обычным делом, и в этом нет ничего исключительного.

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

С учетом сказанного, это не означает, что вы не можете DRY код, который инкапсулирует эти ситуации. Существует довольно большой шанс, что вы сможете это сделать, по крайней мере, в некоторой степени, поскольку это довольно распространенные ситуации.

Итак, что насчет исключений? Ну, первое правило действительно таково: используйте их как можно реже.

Если вам действительно необходимо их использовать, то существует два вида исключений в целом (как я это вижу):

  1. исключения, которые не нарушают общий рабочий процесс пользователя внутри вашего приложения (представьте себе исключение в процедуре генерации миниатюр картинки профиля), и вы можете либо скрыть их от пользователя, либо просто уведомить его о проблеме и ее последствиях, когда это необходимо

  2. исключения, которые вообще не позволяют пользователю использовать приложение. Это последнее средство, которое следует обрабатывать через внутреннюю ошибку сервера 500 в веб-приложениях.

Я склонен использовать метод rescue_from в ApplicationController только для последнего, поскольку для первого типа есть более подходящие места, а ApplicationController, как самый верхний из классов контроллеров, кажется правильным местом для возврата в таких обстоятельствах (хотя в настоящее время какой-нибудь Rack middleware может быть даже более подходящим местом для размещения такой вещи).

-- EDIT --

Конструктивная часть:

Что касается первого, то я бы посоветовал начать использовать find_by_id вместо find, поскольку он не выбрасывает исключение, а возвращает nil в случае неудачи. Тогда ваш код будет выглядеть примерно так:

unless @p = Post.find_by_id(params[:id])
  flash[:error] = "Can't find Blog Post"
end

что гораздо менее болтливо.

Другая распространенная идиома для DRYing такого рода ситуаций - использовать контроллер before_filters для установки часто используемых переменных (например, @p в данном случае). После этого ваш контроллер может выглядеть следующим образом

controller PostsController
  before_filter :set_post, :only => [:create, :show, :destroy, :update]

  def show
      flash[:error] = "Can't find Blog Post" unless @p
  end 

private

  def set_post
    @p = Post.find_by_id(params[:id]) 
  end

end

Что касается второй ситуации (несуществующий последний комментарий), то одним из очевидных решений этой проблемы является перенос всего этого в хелпер:

# This is just your way of finding out the time of the last comment moved into a 
# helper. I'm not saying it's the best one ;)
def last_comment_datetime(post)
  comments = post.comments
  if comments.empty?
    "No comments, yet."
  else
    "Last comment for this post at: #{comments.last.created_at}"
  end
end

Затем, в ваших представлениях, вы просто вызовете

<%= last_comment_datetime(post) %>

Таким образом, крайний случай (пост без комментариев) будет обрабатываться в своем собственном месте и не будет загромождать представление.

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

14
ответ дан 28 November 2019 в 21:58
поделиться

Исключения составляют исключительные обстоятельства. Плохой пользовательский ввод обычно не является исключением; во всяком случае, это довольно распространенное явление. Когда у вас действительно есть исключительные обстоятельства, вы хотите дать себе как можно больше информации. По моему опыту, лучший способ сделать это - строго улучшить обработку исключений на основе опыта отладки. Когда вы сталкиваетесь с исключением, первое, что вам нужно сделать, это написать для него модульный тест. Второе, что вам нужно сделать, это определить, есть ли дополнительная информация, которую можно добавить к исключению. Дополнительная информация в этом случае обычно принимает форму перехвата исключения выше по стеку и либо его обработки, либо создания нового, более информативного исключения, которое имеет преимущество дополнительного контекста. Мое личное правило состоит в том, что мне не нравится перехватывать исключения на более чем трех уровнях стека. Если исключение должно пройти дальше этого, вам нужно поймать его раньше.

Что касается выявления ошибок в пользовательском интерфейсе, операторы if / case полностью подходят, если вы не вкладываете их слишком глубоко. Вот когда такой код становится трудно поддерживать. Вы можете абстрагироваться от этого, если это станет проблемой.

Например:

def flash_assert(conditional, message)
  return true if conditional
  flash[:error] = message
  return false
end

flash_assert(Post.exists?(params[:post_id]), "Can't find Blog Post") or return
1
ответ дан 28 November 2019 в 21:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: