ASP.NET Ядро, обрабатывающее ожидаемые исключения

#c# #asp.net-core

#c# #asp.net-core

Вопрос:

Я работаю над довольно простым CRUD-контроллером в ASP.NET ядро и теперь я сталкиваюсь с проблемой, что мне нужно обрабатывать ошибки, такие как работа с несуществующим объектом (возврат 404) или создание дубликата (возврат 400).

Итак, теперь мне интересно, какой самый идиоматичный способ в ASP.NET Ядро для извлечения ожидаемых ошибок и возврата правильного кода состояния для них.

Одним из способов может быть создание исключений в службах, которые отвечают за фактический CRUD и перехватывают их внутри контроллера:

 /// Inside service...
public void Create(Entity entityDetails) {
  if (entityAlreadyExists(entityDetails)) {
    throw new EntityDuplicateException();
  }
  // ...
}

/// Inside controller...
[HttpPost("{operatorClientId}")] 
public void CreateEntity(Entity entityDetails) {
  try {
   _entityService.CreateEntity(entityDetails);    

    return Ok();
  } catch (EntityDuplicateException e) {  // Some self defined exception type
    return BadRequest(/* Some details about the entity */);
  }
}
  

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

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

 switch (exceptionHandlerPathFeature.Error)
{
    case EntityDuplicateException e:
        context.Response.StatusCode = 400;
        // More details to response here
        break;
    case EntityNotFoundException e:
        context.Response.StatusCode = 404;
        // More details to response here
        break;
    case {} e:
        context.Response.StatusCode = 500;
        // More details to response here
        break;
}
  

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

Комментарии:

1. Вы также могли бы возвращать тип ответа из служб, который включает в себя поле успеха / неудачи, причину сбоя и результат вместо исключений. Тогда вы могли бы просто оценить этот ответ и создать из него соответствующие ответы API. Сделав его универсальным типом, вы также могли бы избежать значительного количества повторяющегося кода. Хотя это было бы очень похоже на использование исключений, это позволяет избежать накладных расходов на создание контекстов исключений.

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

Ответ №1:

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

Вместо того, чтобы ваши методы доступа к данным генерировали исключения, заставьте их возвращать что-то вроде кортежа (bool success, string errorMessage) . В контроллере проверьте success элемент — если это false , верните errorMessage вызывающему.

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