Вопрос

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

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

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

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

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

например

«Список имен клиентов имен клиентов не может быть отображен».

«Получение списка запрошенных вами клиентов не удалось из -за ошибки в базе данных».

«Была ошибка, соединяющая базу данных при получении списка клиентов»

«Не удалось войти в систему для пользователя xx»

Мой вопрос таков:Это ужасно неэффективно (все эти вложенные исключения)?Я подозреваю, что это не лучшая практика, так что мне следует делать, чтобы добиться того же, или мне действительно следует попытаться достичь чего-то лучшего?

Это было полезно?

Решение

Это просто немного ужасно.

Если вы показываете конечному пользователю ошибку, он должен иметь возможность отреагировать на нее.В «Список имен клиентов, которые ваш запрошенный не может быть отображен». Дело, ваш пользователь просто подумает "Ну и что?" Во всех этих случаях просто отобразите сообщение «что -то плохое».Вам даже не нужно перехватывать эти исключения: когда что-то идет не так, позвольте какому-нибудь глобальному методу (например, application_error) обработать это и отобразить общее сообщение.Когда вы или ваш пользователь можете что-то сделать с ошибкой, поймайте ее и выполните действие или уведомите пользователя.

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

Кстати, отображение информации о возникающих ошибках может привести к уязвимостям безопасности.Чем меньше злоумышленники знают о вашей системе, тем меньше вероятность, что они найдут способы ее взломать (вспомните такие сообщения, как «Синтаксическая ошибка в операторе sql:Выберите * От пользователей, где username='a';база данных drp;--'..." ожидается:«дроп» вместо «дрп».Такие сайты больше не делают).

Другие советы

Технически создание новых исключений обходится дорого, однако я не буду обсуждать этот вопрос, поскольку «дорогостоящее» понятие относительное: если вы генерируете 100 таких исключений в минуту, вы, скорее всего, не увидите стоимость;если вы генерируете 1000 таких исключений в секунду, вы вполне можете увидеть снижение производительности (следовательно, не стоит здесь обсуждать - производительность - это ваше дело).

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

  • Что-то ты на самом деле хотеть поделиться с пользователем?
  • Что-то, что ваш пользователь сможет интерпретировать, понимать и использовать?
  • Написано таким образом, чтобы не мешать дальнейшему повторному использованию низкоуровневых компонентов, полезность которых может быть неизвестна при их написании?

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

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

Я бы начал с того, что сначала решил, существует ли действительно семантическая разница между выданным исключением Framework и сообщением об исключении, которое вы хотите предоставить пользователю.Если да, то используйте собственное исключение на самом низком уровне («не удалось войти в систему» ​​в вашем примере).Последующие шаги, вплоть до фактического представления исключения, на самом деле не требуют каких-либо пользовательских исключений.Исключение, которое вас интересует, уже сгенерировано (не удалось войти в систему) — продолжение переноса этого сообщения на каждый уровень стека вызовов не служит никакой реальной цели, кроме раскрытия вашего стека вызовов вашим пользователям.Для этих «средних» шагов, при условии наличия блоков try/catch, простая стратегия «зарегистрировать и выбросить» будет работать нормально.

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

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

Вот пара достойных статей о лучших практиках обработки исключений:

http://www.codeproject.com/KB/architecture/Exceptionbestpractices.aspx

http://aspalliance.com/1119

Боже, это получилось многословно... заранее извиняюсь.

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

Но подождите!Если код фреймворка или библиотека, которую вы используете, выдает исключение, значит, все уже идет наперекосяк.Есть ли у вас нефункциональные требования к тому, как быстро распространяется сообщение об ошибке после исключения?Я сомневаюсь в этом.Действительно ли это имеет большое значение?Произошло нечто непредвиденное и «исключительное».Главное — преподнести пользователю толковую, полезную информацию.

Я думаю, что вы на правильном пути в том, что делаете.

Конечно, это ужасно неэффективно.Но в тот момент, когда возникает исключение, достаточно важное, чтобы его можно было показать конечному пользователю, вам не следует об этом беспокоиться.

Там, где я работаю, у нас есть всего несколько причин ловить исключения.Мы делаем это только тогда, когда...

  1. Мы можем что-то с этим сделать — например, мы знаем, что это может иногда случаться, и можем исправить это в коде по мере того, как это происходит (очень редко).

  2. Мы хотим знать, где это происходит (тогда мы просто повторно выбрасываем исключение).

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top