Почему 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?

Ответ 1

Правильный шаблон - вызвать перегрузку перенаправления с помощью endResponse = false и сделать вызов, чтобы сообщить конвейеру IIS, что он должен перейти непосредственно на этап EndRequest после возврата элемента управления:

Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();

Это сообщение в блоге от Thomas Marquardt содержит дополнительные сведения, в том числе о том, как обрабатывать специальный случай перенаправления внутри обработчика Application_Error.

Ответ 2

В ASP.Net WebForms существует нет простое и элегантное решение проблемы Redirect. Вы можете выбрать между Dirty и Tedious.

Dirty: Response.Redirect(url) отправляет перенаправление в браузер, а затем выдает ThreadAbortedException для завершения текущего потока. Таким образом, код не выполняется после вызова Redirect(). Недостатки: это плохая практика и имеет последствия для производительности, чтобы убивать такие темы. Кроме того, ThreadAbortedExceptions будет отображаться в журнале регистрации исключений.

Tedious. Рекомендуемым способом является вызов Response.Redirect(url, false), а затем Context.ApplicationInstance.CompleteRequest(). Однако выполнение кода будет продолжено, а остальные обработчики событий в жизненном цикле страницы будут выполнены. (Например, если вы выполняете перенаправление в Page_Load, будет выполняться не только остальная часть обработчика, Page_PreRender и т.д., А отображаемая страница просто не будет отправлена ​​в браузер. Вы можете избежать дополнительной обработки с помощью например, установка флага на странице, а затем последующие обработчики событий проверяют этот флаг перед выполнением любой обработки.

(В документации к CompleteRequest указано, что она "заставляет ASP.NET обходить все события и фильтровать в цепочке выполнения HTTP-протокола". Это легко понять неправильно. Оно обходит дополнительные HTTP-фильтры и модули, но это не обходит дальнейшие события в текущем жизненном цикле страницы.)

Более глубокая проблема заключается в том, что WebForms не имеет уровня абстракции. Когда вы находитесь в обработчике событий, вы уже строите страницу для вывода. Перенаправление в обработчике событий является уродливым, потому что вы завершаете частично сгенерированную страницу, чтобы создать другую страницу. MVC не имеет этой проблемы, поскольку поток управления отделен от представлений рендеринга, поэтому вы можете выполнить чистое перенаправление, просто вернув RedirectAction в контроллер, не создавая представление.

Ответ 3

Я знаю, что опаздываю, но у меня была эта ошибка только в том случае, если мой Response.Redirect находится в блоке Try...Catch.

Никогда не помещайте Response.Redirect в блок Try... Catch. Это плохая практика.

Изменить

В ответ на комментарий @Kiquenet, вот что я хотел бы сделать в качестве альтернативы помещению Response.Redirect в блок Try... Catch.

Я бы разложил метод/функцию на два этапа.

Шаг первый внутри блока Try... Catch выполняет запрошенные действия и устанавливает значение "результат" для указания успеха или отказа действий.

Шаг второй за пределами поля "Try... Catch" выполняет перенаправление (или не входит) в зависимости от того, что такое "результат".

Этот код далек от совершенства и, вероятно, его нельзя копировать, так как я его не тестировал

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.
    }
}

Ответ 4

Response.Redirect() выдает исключение, чтобы прервать текущий запрос.

Эта статья статья в KB описывает это поведение (также для методов Request.End() и Server.Transfer()).

Для Response.Redirect() существует перегрузка:

Response.Redirect(String url, bool endResponse)

Если вы передадите endResponse = false, тогда исключение не будет выбрано (но среда выполнения продолжит обработку текущего запроса).

Если endResponse = true (или если используется другая перегрузка), генерируется исключение, и текущий запрос немедленно прекращается.

Ответ 5

Это как раз работает Response.Redirect(url, true). Он бросает ThreadAbortException, чтобы прервать поток. Просто игнорируйте это исключение. (Я полагаю, что это какой-то глобальный обработчик ошибок/регистратор, где вы его видите?)

Интересное связанное обсуждение Является ли Response.End() считающимся вредным?

Ответ 6

Здесь официальная строка по проблеме (я не мог найти последнюю информацию, но я не думаю, что ситуация изменилась позже версии .net)

Ответ 7

Я даже попытался избежать этого, на всякий случай, выполняя Abort в потоке вручную, но я скорее оставлю его с "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();
}

Ответ 8

То, что я делаю, - это исключение, а также другие возможные исключения. Надеюсь, это поможет кому-то.

 catch (ThreadAbortException ex1)
 {
    writeToLog(ex1.Message);
 }
 catch(Exception ex)
 {
     writeToLog(ex.Message);
 }

Ответ 9

Также я пробовал другое решение, но часть кода выполнялась после перенаправления.

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
}

Ответ 10

У меня тоже была эта проблема. Попробуйте использовать Server.Transfer вместо Response.Redirect Работал для меня