Почему Response.Redirect вызывает System.Threading.ThreadAbortException? - PullRequest
224 голосов
/ 06 мая 2010

Когда я использую Response.Redirect (...) для перенаправления моей формы на новую страницу, я получаю сообщение об ошибке:

Первое случайное исключение типа «System.Threading.ThreadAbortException» произошло в mscorlib.dll
Исключение типа «System.Threading.ThreadAbortException» произошло в mscorlib.dll, но не было обработано в коде пользователя

Насколько я понимаю, ошибка вызвана тем, что веб-сервер прерывает оставшуюся часть страницы, на которой был вызван response.redirect.

Я знаю, что могу добавить второй параметр к Response.Redirect, который называется endResponse. Если для параметра endResponse установлено значение True, я все равно получаю сообщение об ошибке, но если установить значение False, то нет. Я почти уверен, что это означает, что веб-сервер запускает остальную часть страницы, с которой я перенаправлен. Что может показаться неэффективным, если не сказать больше. Есть лучший способ сделать это? Что-то кроме Response.Redirect или есть способ заставить старую страницу прекратить загрузку, где я не получу ThreadAbortException?

Ответы [ 10 ]

322 голосов
/ 06 мая 2010

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

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

В этом блоге от Томаса Марквардта содержится дополнительная информация, в том числе о том, как обрабатывать особый случай перенаправления внутри обработчика Application_Error.

151 голосов
/ 18 октября 2012

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

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

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

(Документация к CompleteRequest гласит, что « заставляет ASP.NET обходить все события и фильтрацию в цепочке выполнения конвейера HTTP ». Это может быть неправильно понято. Это обходит дальнейший HTTP фильтры и модули, но не пропускает дальнейшие события в текущем жизненном цикле page .)

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

30 голосов
/ 28 июня 2012

Я знаю, что опоздал, но у меня когда-либо была эта ошибка, только если мой 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.
    }
}
8 голосов
/ 06 мая 2010

Response.Redirect() выдает исключение для отмены текущего запроса.

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

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

Response.Redirect(String url, bool endResponse)

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

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

7 голосов
/ 06 мая 2010

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

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

6 голосов
/ 06 мая 2010

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

2 голосов
/ 03 сентября 2015

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

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

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
}
1 голос
/ 28 февраля 2013

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

 catch (ThreadAbortException ex1)
 {
    writeToLog(ex1.Message);
 }
 catch(Exception ex)
 {
     writeToLog(ex.Message);
 }
0 голосов
/ 25 мая 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...