Обработка исключений в контроллере (ASP.NET MVC)
когда исключение создается вашим собственным кодом, который вызывается из действия в контроллере, как это следует обрабатывать? Я вижу много примеров лучшей практики, где нет никаких операторов try-catch вообще. Например, доступ к данным из хранилища:
public ViewResult Index()
{
IList<CustomModel> customModels = _customModelRepository.GetAll();
return View(customModels);
}
очевидно, что этот код может вызвать исключение, если вызов относится к базе данных, к которой он не может получить доступ, и мы используем ORM, например Entity Framework.
однако все, что я вижу, будет случается, что исключение будет пузыриться и показывать неприятное сообщение об ошибке пользователю.
Я знаю атрибут HandleError, но я понимаю, что он в основном используется для перенаправления вас на страницу ошибок, если происходит исключение, которое не обработано.
конечно, этот код может быть завернут в try-catch, но не отделяется красиво, особенно если у вас больше логики:
public ViewResult Index()
{
if (ValidationCheck())
{
IList<CustomModel> customModels = new List<CustomModel>();
try
{
customModels = _customModelRepository.GetAll();
}
catch (SqlException ex)
{
// Handle exception
}
if (CustomModelsAreValid(customModels))
// Do something
else
// Do something else
}
return View();
}
ранее я извлек весь код, который может создавать исключения, такие как база данных вызывает класс DataProvider, который обрабатывает ошибки и возвращает сообщения для отображения сообщений пользователю.
мне было интересно, что лучший способ справиться с этим? Я не всегда хочу возвращаться на страницу ошибок, потому что некоторые исключения не должны этого делать. Вместо этого сообщение об ошибке для пользователя должны отображаться в обычном режиме. Был ли мой предыдущий метод правильным или есть лучшее решение?
4 ответов
Я делаю три вещи, чтобы отобразить более дружественные сообщения:
- воспользуйтесь глобальным обработчиком исключений. В случае MVC: Application_Error в глобальном.асакс. Узнайте, как использовать его здесь:http://msdn.microsoft.com/en-us/library/24395wz3 (v=против 100).aspx
- I подкласс исключения в исключение UserFriendlyException. Я делаю все возможное во всех моих базовых классах обслуживания, чтобы бросить это UserFriendlyException вместо простого старого исключения. Я всегда стараюсь помещать пользовательские сообщения в эти пользовательские исключения. Основной целью которого является возможность проверки типа исключения в методе Application_Error. Для UserFriendlyExceptions я просто использую удобное сообщение, которое я установил глубоко в своих услугах ,например: "Эй! 91 градуса не является допустимым значением широты!". Если это обычное исключение, то это какой-то случай, который я не обрабатывал, поэтому я отображаю более общее сообщение об ошибке, например: "Ой, что-то пошло не так! Мы сделайте все возможное, чтобы это исправить!".
- Я также создаю ErrorController, который отвечает за рендеринг удобных для пользователя представлений или JSON. Это контроллер, методы которого будут вызываться из метода Application_Error.
EDIT: Я подумал, что стоит упомянуть ASP.NET веб-API, поскольку он тесно связан. Поскольку конечные точки веб-API не обязательно будут браузером, мне нравится иметь дело с ошибками немного по-другому. Я все еще использую "FriendlyException" (#2 выше), но вместо перенаправления на ErrorController, я просто позволяю всем моим конечным точкам возвращать какой-то базовый тип, содержащий свойство Error. Итак, если исключение пузырится вплоть до контроллеров веб-API, я обязательно вставляю эту ошибку в свойство Error ответа API. Это сообщение об ошибке будет либо дружественным сообщением, которое пузырится из классов, на которые опирается контроллер API, либо общим сообщением, Если тип исключения не в FriendlyException. Таким образом, потребляющий клиент может просто проверить, является ли свойство Error ответа API пустым. Отображение сообщения если ошибка присутствует, действуйте как обычно, если нет. Хорошо, что из-за концепции дружественного сообщения сообщение может быть гораздо более значимым для пользователя, чем общая "ошибка!" сообщение. Я использую эту стратегию при написании мобильных приложений с Xamarin, где я могу поделиться своими типами C# между моими веб-службами и моим приложением iOS/Android.
С Asp.Net MVC также можно переопределить метод OnException для контроллера.
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled)
{
return;
}
filterContext.Result = new ViewResult
{
ViewName = ...
};
filterContext.ExceptionHandled = true;
}
это позволяет перенаправить на пользовательскую страницу ошибки с сообщением, которое ссылается на исключение, если вы хотите.
я использовал переопределение OnException, потому что у меня есть несколько проектов, ссылающихся на тот, у которого есть контроллер, который обрабатывает ошибки:
Безопасность / HandleErrorsController.cs
protected override void OnException(ExceptionContext filterContext)
{
MyLogger.Error(filterContext.Exception); //method for log in EventViewer
if (filterContext.ExceptionHandled)
return;
filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult
{
Data = new
{
Success = false,
Error = "Please report to admin.",
ErrorText = filterContext.Exception.Message,
Stack = filterContext.Exception.StackTrace
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.ExceptionHandled = true;
}
все подобные вопросы не очень конструктивны, потому что ответ всегда "зависит", потому что существует так много способов обработки ошибок.
многие люди любят использовать метод HandleError, потому что любое исключение в основном не восстанавливается. Я имею в виду, что вы собираетесь делать, если вы не можете вернуть объекты? Ты собираешься показать им ошибку в любом случае, не так ли?
возникает вопрос, как вы хотите показать их ошибки. Если показывать им страница ошибки приемлема, чем HandleError работает нормально, и обеспечивает легкое место для регистрации ошибки. Если вы используете Ajax или хотите что-то более причудливое, вам нужно разработать способ сделать это.
вы говорите о классе DataProvider. Это в основном то, что ваш репозиторий. Почему бы не встроить это в свой репозиторий?