Как регистрировать ВСЕ исключения во всем мире для приложения С# MVC4 WebAPI?
Фон
Я разрабатываю уровень обслуживания API для клиента, и мне было предложено поймать и зарегистрировать все ошибки по всему миру.
Итак, хотя что-то вроде неизвестной конечной точки (или действия) легко обрабатывается с помощью ELMAH или добавлением чего-то вроде этого в Global.asax
:
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
//do more stuff
}
.,. Необработанные ошибки, не связанные с маршрутизацией, не регистрируются. Например:
public class ReportController : ApiController
{
public int test()
{
var foo = Convert.ToInt32("a");//Will throw error but isn't logged!!
return foo;
}
}
Я также попытался установить атрибут [HandleError]
глобально, зарегистрировав этот фильтр:
filters.Add(new HandleErrorAttribute());
Но это также не регистрирует все ошибки.
Проблема/Вопрос
Как перехватить ошибки, подобные тем, которые были созданы при вызове /test
выше, чтобы я мог их зарегистрировать? Кажется, что этот ответ должен быть очевиден, но я пробовал все, о чем я могу думать до сих пор.
В идеале я хочу добавить некоторые вещи в журнал регистрации ошибок, такие как IP-адрес запрашивающего пользователя, дата, время и т.д. Я также хочу иметь возможность автоматически отправлять сотрудникам службы поддержки автоматически при возникновении ошибки. Все это я могу сделать, если только я могу перехватить эти ошибки, когда они произойдут!
РЕШЕНО!
Благодаря Дарину Димитрову, чей ответ я принял, я понял это. WebAPI делает не обрабатывать ошибки так же, как обычный контроллер MVC.
Вот что сработало:
1) Добавьте собственный фильтр в пространство имен:
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is BusinessException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Exception"
});
}
//Log Critical errors
Debug.WriteLine(context.Exception);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}
2) Теперь зарегистрируйте фильтр по всему миру в классе WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
config.Filters.Add(new ExceptionHandlingAttribute());
}
}
ИЛИ вы можете пропустить регистрацию и просто украсить один контроллер атрибутом [ExceptionHandling]
.
Ответы
Ответ 1
Если ваш веб-API размещен внутри приложения ASP.NET, событие Application_Error
будет вызвано для всех необработанных исключений в вашем коде, включая те, которые вы указали в тестовом действии. Итак, все, что вам нужно сделать, это обработать это исключение внутри события Application_Error. В примере кода, который вы указали, вы обрабатываете исключение типа HttpException
, что, очевидно, не относится к коду Convert.ToInt32("a")
. Поэтому убедитесь, что вы регистрируете и обрабатываете все исключения там:
protected void Application_Error()
{
Exception unhandledException = Server.GetLastError();
HttpException httpException = unhandledException as HttpException;
if (httpException == null)
{
Exception innerException = unhandledException.InnerException;
httpException = innerException as HttpException;
}
if (httpException != null)
{
int httpCode = httpException.GetHttpCode();
switch (httpCode)
{
case (int)HttpStatusCode.Unauthorized:
Response.Redirect("/Http/Error401");
break;
// TODO: don't forget that here you have many other status codes to test
// and handle in addition to 401.
}
else
{
// It was not an HttpException. This will be executed for your test action.
// Here you should log and handle this case. Use the unhandledException instance here
}
}
}
Забастовкa >
Обработка исключений в веб-API может выполняться на разных уровнях. Здесь detailed article
объясняется различные возможности:
-
атрибут фильтра специальных исключений, который может быть зарегистрирован как глобальный фильтр исключений
[AttributeUsage(AttributeTargets.All)]
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is BusinessException)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Exception"
});
}
//Log Critical errors
Debug.WriteLine(context.Exception);
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("An error occurred, please try again or contact the administrator."),
ReasonPhrase = "Critical Exception"
});
}
}
-
пользовательский action invoker
public class MyApiControllerActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, System.Threading.CancellationToken cancellationToken)
{
var result = base.InvokeActionAsync(actionContext, cancellationToken);
if (result.Exception != null && result.Exception.GetBaseException() != null)
{
var baseException = result.Exception.GetBaseException();
if (baseException is BusinessException)
{
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(baseException.Message),
ReasonPhrase = "Error"
});
}
else
{
//Log critical error
Debug.WriteLine(baseException);
return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent(baseException.Message),
ReasonPhrase = "Critical Error"
});
}
}
return result;
}
}
Ответ 2
В дополнение к предыдущим ответам.
Вчера ASP.NET Web API 2.1 был официально опубликован .
Он предлагает еще одну возможность обрабатывать исключения во всем мире.
Подробности приведены в sample.
Вкратце, вы добавляете глобальные журналы исключения и/или глобальный обработчик исключений (только один).
Вы добавляете их в конфигурацию:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// There can be multiple exception loggers.
// (By default, no exception loggers are registered.)
config.Services.Add(typeof(IExceptionLogger), new ElmahExceptionLogger());
// There must be exactly one exception handler.
// (There is a default one that may be replaced.)
config.Services.Replace(typeof(IExceptionHandler), new GenericTextExceptionHandler());
}
И их реализация:
public class ElmahExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
...
}
}
public class GenericTextExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new InternalServerErrorTextPlainResult(
"An unhandled exception occurred; check the log for more information.",
Encoding.UTF8,
context.Request);
}
}
Ответ 3
Почему ретролл и т.д.? Это работает, и это сделает статус возврата службы 500 и т.д.
public class LogExceptionFilter : ExceptionFilterAttribute
{
private static readonly ILog log = LogManager.GetLogger(typeof (LogExceptionFilter));
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
log.Error("Unhandeled Exception", actionExecutedContext.Exception);
base.OnException(actionExecutedContext);
}
}
Ответ 4
Оберните все это в try/catch и запишите необработанное исключение, а затем передайте его. Если нет лучшего встроенного способа сделать это.
Здесь ссылка Исправить все (обработанные или необработанные) Исключения
(изменить: oh API)
Ответ 5
Вы думали о том, чтобы сделать что-то вроде фильтра ошибок с ошибкой, например
[HandleError]
public class BaseController : Controller {...}
вы также можете создать пользовательскую версию [HandleError]
, с помощью которой вы можете записывать информацию об ошибке и все другие данные в журнал