Por que Response.Redirect faz com que System.Threading.ThreadAbortException?

Quando eu uso Response.Redirect (…) para redirect meu formulário para uma nova página, recebo o erro:

Uma primeira chance exceção do tipo ‘System.Threading.ThreadAbortException’ ocorreu em mscorlib.dll
Uma exceção do tipo ‘System.Threading.ThreadAbortException’ ocorreu em mscorlib.dll, mas não foi tratada no código do usuário

Meu entendimento disso é que o erro está sendo causado pelo servidor da web abortando o restante da página em que o response.redirect foi chamado.

Eu sei que posso adicionar um segundo parâmetro para Response.Redirect que é chamado endResponse. Se eu definir endResponse como True, ainda recebo o erro, mas se eu configurá-lo como False, não o faço. Eu tenho certeza que isso significa que o servidor está executando o restante da página que eu redirecionei para longe. O que parece ser ineficiente para dizer o mínimo. Existe uma maneira melhor de fazer isso? Algo diferente de Response.Redirect ou existe uma maneira de forçar a página antiga para parar de carregar onde eu não vou obter um ThreadAbortException ?

O padrão correto é chamar a sobrecarga Redirect com endResponse = false e fazer uma chamada para informar ao pipeline do IIS que deve avançar diretamente para o estágio EndRequest quando você retornar o controle:

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

Esta postagem de blog de Thomas Marquardt fornece detalhes adicionais, incluindo como lidar com o caso especial de redirecionamento dentro de um manipulador Application_Error.

Não há nenhuma solução simples e elegante para o problema de Redirect em ASP.Net WebForms. Você pode escolher entre a solução Dirty e a solução Tedious

Dirty : Response.Redirect(url) envia um redirecionamento para o navegador e, em seguida, lança um ThreadAbortedException para finalizar o segmento atual. Portanto, nenhum código é executado após a chamada Redirect (). Desvantagens: É uma má prática e tem implicações de desempenho para matar tópicos como este. Além disso, ThreadAbortedExceptions será exibido no log de exceções.

Tedious : A maneira recomendada é chamar Response.Redirect(url, false) e Context.ApplicationInstance.CompleteRequest() Entretanto, a execução do código continuará e o restante dos manipuladores de events no ciclo de vida da página ainda será executado. (Por exemplo, se você executar o redirecionamento em Page_Load, não apenas o resto do manipulador será executado, Page_PreRender e assim por diante também será chamado – a página renderizada simplesmente não será enviada para o navegador. Você pode evitar o processamento extra por exemplo, definir um sinalizador na página e permitir que os manipuladores de events subsequentes verifiquem esse sinalizador antes de fazer qualquer processamento.

(A documentação para CompleteRequest afirma que ” Faz com que o ASP.NET ignore todos os events e filtragem na cadeia de execução do pipeline HTTP “. Isso pode ser facilmente entendido erroneamente. Ele ignora filtros e módulos HTTP adicionais, mas não ignora mais events no ciclo de vida da página atual.)

O problema mais profundo é que o WebForms não possui um nível de abstração. Quando você está em um manipulador de events, já está no processo de construir uma página para saída. Redirecionar em um manipulador de events é feio porque você está finalizando uma página parcialmente gerada para gerar uma página diferente. O MVC não tem esse problema, pois o stream de controle é separado das visualizações de renderização, portanto, você pode fazer um redirecionamento limpo simplesmente retornando uma RedirectAction no controlador, sem gerar uma visualização.

Eu sei que estou atrasado, mas eu só tive esse erro se o meu Response.Redirect está em um bloco Try...Catch .

Nunca coloque um Response.Redirect em um bloco Try … Catch. É uma má prática

Editar

Em resposta ao comentário do @ Kiquenet, aqui está o que eu faria como uma alternativa para colocar o Response.Redirect no bloco Try … Catch.

Eu dividiria o método / function em duas etapas.

O primeiro passo dentro do bloco Try … Catch executa as ações solicitadas e define um valor “result” para indicar sucesso ou falha das ações.

O segundo passo fora do bloco Try … Catch faz o redirecionamento (ou não) dependendo do valor do “resultado”.

Este código está longe de ser perfeito e provavelmente não deve ser copiado já que eu não testei

 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() lança uma exceção para anular o pedido atual.

Este artigo da KB descreve esse comportamento (também para os methods Request.End() e Server.Transfer() ).

Para Response.Redirect() existe uma sobrecarga:

 Response.Redirect(String url, bool endResponse) 

Se você passar endResponse = false , a exceção não será lançada (mas o tempo de execução continuará processando a solicitação atual).

Se endResponse = true (ou se a outra sobrecarga for usada), a exceção será lançada e a solicitação atual será encerrada imediatamente.

É assim que o Response.Redirect (url, true) funciona. Ele lança o ThreadAbortException para anular o segmento. Apenas ignore essa exceção. (Eu presumo que é algum manipulador de erro global / logger onde você o vê?)

Uma interessante discussão relacionada É Response.End () considerado prejudicial?

Aqui está a linha oficial sobre o problema (não consegui encontrar o mais recente, mas não acho que a situação tenha mudado para versões posteriores do .net)

Eu até tentei evitar isso, apenas no caso de fazer o abort no thread manualmente, mas eu prefiro deixá-lo com o “CompleteRequest” e seguir em frente – meu código tem comandos de retorno após os redirecionamentos de qualquer maneira. Então isso pode ser feito

 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(); } 

O que eu faço é pegar essa exceção, junto com outras exceções possíveis. Espero que isso ajude alguém.

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

Também tentei outra solução, mas parte do código executado após o redirecionamento.

 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(); } 

Então, se precisar impedir a execução de código após o redirecionamento

 try { //other code Response.Redirect("") // code not to be executed } catch(ThreadAbortException){}//do there id nothing here catch(Exception ex) { //Logging } 

Eu também tive esse problema. Tente usar o Server.Transfer em vez de Response.Redirect Trabalhado para mim