Почему Response.Redirect вызывает System.Threading.ThreadAbortException?
Когда я использую Response.Redirect(...) для перенаправления моей формы на новую страницу, я получаю сообщение об ошибке:
Первое случайное исключение типа "System.Threading.ThreadAbortException" произошло в mscorlib.dll
Исключение типа "System.Threading.ThreadAbortException" произошло в mscorlib.dll, но не было обработано в коде пользователя
Насколько я понимаю, эта ошибка вызвана тем, что веб-сервер прерывает оставшуюся часть страницы, на которой был вызван response.redirect.
Я знаю, что могу добавить второй параметр Response.Redirect
это называется endResponse. Если для параметра endResponse установлено значение True, я все равно получаю сообщение об ошибке, но если для параметра установлено значение False, то нет. Хотя я уверен, что это означает, что веб-сервер запускает остальную часть страницы, с которой я перенаправлен. Что может показаться неэффективным, если не сказать больше. Есть лучший способ сделать это? Что-то кроме Response.Redirect
или есть способ заставить старую страницу прекратить загрузку, где я не получу ThreadAbortException
?
11 ответов
Правильный шаблон - вызвать перегрузку Redirect с endResponse=false и выполнить вызов, чтобы сообщить конвейеру IIS, что он должен перейти непосредственно к этапу EndRequest после возврата элемента управления:
Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();
Этот пост от Томаса Марквардта содержит дополнительные сведения, в том числе о том, как обрабатывать особый случай перенаправления внутри обработчика Application_Error.
Простого и элегантного решения Redirect
проблема в ASP.Net WebForms. Вы можете выбрать между грязным решением и утомительным решением
Грязный: Response.Redirect(url)
отправляет перенаправление в браузер, а затем выдает ThreadAbortedException
завершить текущий поток. Таким образом, никакой код не выполняется после вызова Redirect(). Недостатки: это плохая практика, и это может повлиять на производительность для уничтожения подобных потоков. Также, ThreadAbortedExceptions
будет отображаться в журнале исключений.
Утомительно: рекомендуемый способ - позвонить Response.Redirect(url, false)
а потом Context.ApplicationInstance.CompleteRequest()
Однако выполнение кода будет продолжено, а остальные обработчики событий в жизненном цикле страницы будут по-прежнему выполняться. (Например, если вы выполняете перенаправление в Page_Load, не только будет выполняться остальная часть обработчика, также будет вызываться Page_PreRender и т. Д. - отображаемая страница просто не будет отправлена в браузер. Вы можете избежать дополнительной обработки, выполнив например, установить флаг на странице, а затем позволить последующим обработчикам событий проверить этот флаг перед выполнением какой-либо обработки.
(Документация к CompleteRequest
заявляет, что " заставляет ASP.NET обходить все события и фильтрацию в цепочке выполнения конвейера HTTP ". Это легко может быть неправильно понято. Он обходит другие фильтры и модули HTTP, но не обходит дальнейшие события в текущем жизненном цикле страницы.)
Более глубокая проблема заключается в том, что в WebForms отсутствует уровень абстракции. Когда вы находитесь в обработчике событий, вы уже находитесь в процессе создания страницы для вывода. Перенаправление в обработчике событий ужасно, потому что вы завершаете частично сгенерированную страницу, чтобы создать другую страницу. У MVC нет этой проблемы, поскольку поток управления отделен от представлений рендеринга, поэтому вы можете выполнить чистое перенаправление, просто возвратив RedirectAction
в контроллере, без генерации представления.
Я знаю, что опоздал, но у меня когда-либо была эта ошибка, только если Response.Redirect
находится в Try...Catch
блок.
Никогда не помещайте Response.Redirect в блок Try...Catch. Это плохая практика
редактировать
В ответ на комментарий @Kiquenet вот что я хотел бы сделать в качестве альтернативы помещению Response.Redirect в блок Try...Catch.
Я бы разбил метод / функцию на два этапа.
Первый шаг внутри блока Try...Catch выполняет запрошенные действия и устанавливает значение "result", указывающее на успех или неудачу действий.
Шаг два за пределами блока Try...Catch выполняет перенаправление (или не делает) в зависимости от значения "result".
Этот код далек от совершенства и, вероятно, его не следует копировать, поскольку я его не проверял
public void btnLogin_Click(UserLoginViewModel model)
{
bool ValidLogin = false; // this is our "result value"
try
{
using (Context Db = new Context)
{
User User = new User();
if (String.IsNullOrEmpty(model.EmailAddress))
ValidLogin = false; // no email address was entered
else
User = Db.FirstOrDefault(x => x.EmailAddress == model.EmailAddress);
if (User != null && User.PasswordHash == Hashing.CreateHash(model.Password))
ValidLogin = true; // login succeeded
}
}
catch (Exception ex)
{
throw ex; // something went wrong so throw an error
}
if (ValidLogin)
{
GenerateCookie(User);
Response.Redirect("~/Members/Default.aspx");
}
else
{
// do something to indicate that the login failed.
}
}
Response.Redirect()
выдает исключение для отмены текущего запроса.
Эта статья KB описывает это поведение (также для Request.End()
а также Server.Transfer()
методы).
За Response.Redirect()
существует перегрузка:
Response.Redirect(String url, bool endResponse)
Если вы передаете endResponse = false, то исключение не генерируется (но среда выполнения продолжит обработку текущего запроса).
Если endResponse = true (или если используется другая перегрузка), генерируется исключение, и текущий запрос будет немедленно прекращен.
Вот официальная строка по проблеме (я не смог найти последнюю версию, но я не думаю, что ситуация изменилась для более поздних версий.net)
Так работает Response.Redirect(url, true). Выдает исключение ThreadAbortException для прерывания потока. Просто игнорируйте это исключение. (Я предполагаю, что это какой-то глобальный обработчик ошибок / регистратор, где вы его видите?)
Интересная связанная с этим дискуссия Является ли Response.End() вредным?
Я даже пытался избежать этого, на всякий случай делая прерывание в потоке вручную, но я предпочитаю оставить его с "CompleteRequest" и двигаться дальше - мой код имеет команды возврата после перенаправлений в любом случае. Так что это можно сделать
public static void Redirect(string VPathRedirect, global::System.Web.UI.Page Sender)
{
Sender.Response.Redirect(VPathRedirect, false);
global::System.Web.UI.HttpContext.Current.ApplicationInstance.CompleteRequest();
}
Также я пробовал другое решение, но часть кода выполнялась после перенаправления.
public static void ResponseRedirect(HttpResponse iResponse, string iUrl)
{
ResponseRedirect(iResponse, iUrl, HttpContext.Current);
}
public static void ResponseRedirect(HttpResponse iResponse, string iUrl, HttpContext iContext)
{
iResponse.Redirect(iUrl, false);
iContext.ApplicationInstance.CompleteRequest();
iResponse.BufferOutput = true;
iResponse.Flush();
iResponse.Close();
}
Так что если нужно предотвратить выполнение кода после перенаправления
try
{
//other code
Response.Redirect("")
// code not to be executed
}
catch(ThreadAbortException){}//do there id nothing here
catch(Exception ex)
{
//Logging
}
Что я делаю, так это ловлю это исключение вместе с другими возможными исключениями. Надеюсь, это поможет кому-то.
catch (ThreadAbortException ex1)
{
// do nothing
}
catch(Exception ex)
{
writeToLog(ex.Message);
}
У меня тоже была эта проблема. Попробуйте использовать Server.Transfer вместо Response.Redirect
Сегодня у меня была такая же проблема. В моем проекте у меня было 2 типа страницы журнала (простой пользователь и пользователь-врач). На прошлой неделе я закончил простую страницу входа пользователя, и она работала без каких-либо проблем. На этой неделе я открыл страницу входа доктора. После того, как я его закончил, я снова проверил простой вход пользователя и обнаружил, что он не работает. Я проследил и понял, что проблема в response.redirect("simpleuser.aspx") (не переходите к simpleuser.aspx). Ошибка была (Невозможно оценить выражение, потому что код оптимизирован или собственный фрейм находится поверх стека вызовов.) Я проверил все решения через Интернет, не повезло ... даже "response.redirect(URL,false)", что я сделал, я просто скопировал simpleuser.aspx в другой каталог .... вот и все. Готово. :)