Ответ
Обработка исключений в сложном проекте должна быть многоуровневой и стратегической.
Ключевые принципы:
-
Обрабатывайте на том уровне, где есть достаточный контекст для принятия решения.
- Низкий уровень (DAL, инфраструктура): Ловите технические исключения (например,
SqlException,IOException), логируйте их и преобразуйте в доменные или пользовательские исключения более высокого уровня.// Пример в репозитории public Customer GetCustomer(int id) { try { // ... вызов базы данных } catch (SqlException ex) { _logger.LogError(ex, "Ошибка БД при получении клиента {Id}", id); throw new DataAccessException("Не удалось загрузить данные клиента", ex); } }
- Низкий уровень (DAL, инфраструктура): Ловите технические исключения (например,
-
Используйте глобальные обработчики (Middleware/фильтры) для непредвиденных исключений.
- В ASP.NET Core —
UseExceptionHandlermiddleware или фильтры исключений. Их задача — залогировать критическую ошибку и вернуть пользователю общий форматированный ответ (например, 500 Internal Server Error), не раскрывая деталей реализации.
- В ASP.NET Core —
-
Не обрабатывайте исключения, которые не можете обработать осмысленно. Иногда правильнее позволить исключению всплыть наверх к глобальному обработчику, чем "глушить" его пустым
catch. -
Разделяйте бизнес-ошибки и системные исключения. Для ожидаемых нарушений бизнес-правил ("недостаточно средств", "пользователь не найден") предпочтительнее использовать возврат результата с ошибкой (например, через
Result<T>илиValidationProblem), а не механизм исключений для контроля потока выполнения.
Ответ 18+ 🔞
Давай разберём эту тему про исключения, а то народ их как огня боится, а потом получается код, который падает от чиха и отлаживать его — это как искать иголку в стоге сена, причём сена ещё и нет.
Смотри, представь себе многоэтажку. На первом этаже у тебя подвал — это база данных, файловая система, всякая низкоуровневая хуйня. На чердаке — пользовательский интерфейс. Так вот, если в подвале прорвало трубу, не надо бежать на чердак и орать «У нас потоп!». Ты должен на уровне подвала сказать: «Э, ребята, тут вода, разбирайтесь». А уже на верхних этажах решат — то ли ведрами вычерпывать, то ли эвакуировать жильцов.
Вот основные правила, чтобы не облажаться:
-
Лови там, где можешь что-то сделать. Если ты в репозитории и база данных плюнула в тебя
SqlException, ты не должен просто пробросить эту ошибку наверх. Наверху не поймут, что это за «Timeout expired» такой. Ты должен её обернуть во что-то осмысленное для твоего домена.// Допустим, в репозитории public Customer GetCustomer(int id) { try { // ... лезем в базу } catch (SqlException ex) // Ловим конкретную техническую хрень { _logger.LogError(ex, "База данных взбунтовалась при запросе клиента {Id}", id); // Превращаем в своё, понятное исключение throw new DataAccessException("Не удалось загрузить клиента из-за проблем с БД", ex); } }Теперь на уровень выше поймают не абракадабру от SQL-драйвера, а внятное
DataAccessException. Уже лучше. -
Поставь глобальную сетку. Это как пожарная система в здании. В ASP.NET Core — это
UseExceptionHandler. Его задача — поймать ВСЁ, что долетело до самого верха и не было обработано. Что он делает? Пишет в лог всё, включая трассировку стека (потому что тебе, разработчику, это надо), а пользователю показывает какую-нибудь вежливую заглушку: «Упс, что-то пошло не так, мы уже в курсе». Никаких деталей, иначе любой проходимец узнает про твою систему больше, чем ты сам. -
Не глуши всё подряд пустым catch. Это, блядь, главная болезнь! Видишь
try { ... } catch { }— бей по рукам. Если ты поймал исключение и не знаешь, что с ним делать (не можешь восстановить работу, не можешь преобразовать, не залогировал), то НЕ ЛОВИ ЕГО. Пусть летит наверх к глобальному обработчику. Заткнуть его и сделать вид, что ничего не было — это гарантировать, что программа будет работать неправильно, а ты об этом даже не узнаешь. Пиздец, а не подход. -
Разделяй ошибки бизнеса и аварии системы. Это важно, ёпта! Если пользователь пытается снять больше денег, чем есть на счету — это не исключение, это нормальная бизнес-ситуация. Не надо кидать
InsufficientFundsExceptionдля контроля потока. Лучше верни объектOperationResultс флагомIsSuccessи сообщением «Недостаточно средств». Исключения — это для НЕНОРМАЛЬНЫХ, аварийных ситуаций (диск полный, сеть отвалилась, конфиг кривой). Использовать исключения для обычной логики — это как молотком гвозди забивать: вроде работает, но стена вся в дырах, и соседи жалуются.
Итог такой: выстраивай оборону слоями. На нижнем — лови технический мусор и превращай во что-то осмысленное. В середине — обрабатывай то, что можно исправить. А на самом верху — лови всё остальное, логируй и показывай юзеру, что ты не свинья, а всё под контролем. И перестань бояться, что что-то упадёт. Лучше упадёт с понятной ошибкой в логе, чем будет тихо и непредсказуемо глючить.