Я создал специальный обработчик исключений для веб-API, например:
public class MyGlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
// here I handle them all, no matter sync or not
}
public override Task HandleAsync(ExceptionHandlerContext context,
CancellationToken cancellationToken)
{
// not needed, but I left it to debug and find out why it never reaches Handle() method
return base.HandleAsync(context, cancellationToken);
}
public override bool ShouldHandle(ExceptionHandlerContext context)
{
// not needed, but I left it to debug and find out why it never reaches Handle() method
return context.CatchBlock.IsTopLevel;
}
}
Я регистрирую его в своем приложении Global.asax Application_Start:
GlobalConfiguration.Configuration.Services.Replace(typeof(IExceptionHandler),
new MyGlobalExceptionHandler());
Все работало нормально, независимо от того, где я выбрал исключения - методы внутреннего контроллера или мои пользовательские атрибуты выше методов контроллера, и неважно, вызываю ли я его из запросов AJAX или непосредственно из браузера.
Но однажды мне понадобилась поддержка CORS для моих запросов AJAX. Я включил CORS во всем мире, как описано в статье Включение запросов перекрестного происхождения в веб-API ASP.NET
var cors = new EnableCorsAttribute("*", "*", "*"); // * is just for debugging purpose
config.EnableCors(cors);
Сначала все казалось ОК, CORS работал, как ожидалось, сервер ответил на запрос OPTIONS.
Но проблемы начались, когда я хотел проверить свою аутентификацию. Внезапно исключение было проглочено, и я получил пустой ответ JSON {}
вместо моего настраиваемого исключения JSON, которое я создаю в методе Handle() моего MyGlobalExceptionHandler.
Во время отладки я с удивлением обнаружил, что теперь для запросов AJAX метод ShouldHandle() вызывается только с IsTopLevel = false, поэтому исключение никогда не пузырится и никогда не достигает моего метода Handle(). Как только я отключу CORS, все работает отлично (за исключением междоменных запросов, конечно).
Почему IsTopLevel никогда не будет правдой, если я включу CORS? Как это исправить?
Еще один побочный эффект заключается в следующем. Если CORS отключен, то, если я исключу исключение из метода Handle(), он достигнет обработчика Application_Error в Global.asax. Но если я включу CORS и исключаю исключения из методов моего обработчика, эти исключения никогда не достигают Application_Error.
ОБНОВЛЕНО БОЛЬШЕ ДЕТАЛЕЙ:
Кажется, я нашел точно, когда это происходит.
Если я выбрал исключение в методе контроллера, когда CORS включен, то CORS вообще не ударит и не отправит заголовок Access-Control-Allow-Origin. Когда браузер не получает заголовок, он немедленно нарушает запрос, и это нарушение, похоже, влияет и на обработчик исключений - он никогда не достигает метода ShouldHandle() с IsTopLevel = true. Chrome и Mozilla действуют так же, даже когда я запускаю запрос AJAX из локального html файла в мой websiet на локальном хосте IIS Express.
Но IE 11 отличается от IE 11. Когда я открываю html файл там, он сначала запрашивает у меня разрешения на включение сценариев. После того, как я согласен, IE 11 игнорирует тот факт, что нет заголовков CORS, и он не прерывает запрос, поэтому мой обработчик исключений получает IsTopLevel = true и способен возвращать настраиваемый ответ об ошибке.
Я предполагаю, что это должно быть исправлено в ядре веб-API - даже если я делаю исключения, CORS все равно сможет пинать и отправлять свои заголовки, поэтому браузер принимает ответ. Я создал минимальное тестовое приложение, и я отправлю его команде ASP.NET на CodePlex. Ссылка на тестовый проект. (файл zip проекта будет отмечен, просто нажмите "Загрузить" и проигнорируйте все остальные файлы в этой папке)