Como evitar Response.End () Exceção “Thread estava sendo abortado” durante o download do arquivo Excel


96

Eu tentei converter meu conjunto de dados em Excel e fazer o download desse Excel. Consegui meu arquivo Excel necessário. Mas System.Threading.ThreadAbortException foi gerado a cada download do Excel. Como resolver esse problema? .. Por favor me ajude ...

Eu chamo esse método na minha tela aspx. Também há mesma exceção lançada por esse método.

Eu chamo essa função public void ExportDataSet (DataSet ds) em muitas telas aspx e também estou mantendo o método de logger de erros para exceções que são geradas em tempo de execução, essas exceções são gravadas em arquivos .txt. Portanto, essa mesma exceção é registrada em todos os arquivos txt da tela do aspx. Eu só quero evitar que essa exceção lance do arquivo de classe declarado do método para o aspx. Simplesmente, eu só quero lidar com essa exceção em meu próprio arquivo de classe de declaração de método.

Chamada do método de arquivo ASPX: excel.ExportDataSet (dsExcel);

Definição do Método:

public void ExportDataSet(DataSet ds)
{

   try
   {
      string filename = "ExcelFile.xls";
      HttpResponse response = HttpContext.Current.Response;
      response.Clear();
      response.Charset = "";
      response.ContentType = "application/vnd.ms-excel";
      response.AddHeader("Content-Disposition", "attachment;filename=\"" + filename + "\"");
      using (StringWriter sw = new StringWriter())
      {
         using (HtmlTextWriter htw = new HtmlTextWriter(sw))
         {
             GridView dg = new GridView();
             dg.DataSource = ds.Tables[0];
             dg.DataBind();
             dg.RenderControl(htw);
             // response.Write(style);
             response.Write(sw.ToString());                                                
             response.End();                    // Exception was Raised at here
         }
      }
   }
   catch (Exception ex)
   {
      string Err = ex.Message.ToString();
      EsHelper.EsADLogger("HOQCMgmt.aspx ibtnExcelAll_Click()", ex.Message.ToString());
   }
   finally
   {                
   }
}

2
Não use, Response.Endconsulte stackoverflow.com/a/3917180/2864740 (e as outras respostas); observe que a exceção é "esperada", pois é como a pilha é desfeita (portanto, não capture essa exceção). Se você ainda quiser capturar [outras] exceções, use:.. catch (ThreadAbortException) { throw; /* propagate */ } catch (Exception ex) { .. }
user2864740

Só por curiosidade, qual logger você está usando
rogue39nin

Respostas:


194

Pesquisei online e vi que Response.End()sempre lança uma exceção.

Substitua este: HttpContext.Current.Response.End();

Com isso:

HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
HttpContext.Current.Response.SuppressContent = true;  // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.

2
Uau, uma dádiva de Deus. Isso me economizou horas de depuração usando WinDbg. No meu caso, meu w3wp.exe travava se houvesse muitos ThreadAbortException
Dio Phung

Obrigado. Este trecho de código é realmente útil se você quiser adicionar alguma verificação de autorização ao construtor de serviço asmx
vadim

Isso funcionou para mim. Substituí .End () pelo código sugerido e agora funciona sem exceção. Obrigado, Meu código de trabalho agora é: Response.ContentType = "text / csv"; Response.AddHeader ("Content-Disposition", string.Format ("attachment; filename = \" {0} \ "", Path.GetFileName (filePath))); Response.TransmitFile (filePath); //Response.End (); HttpContext.Current.Response.Flush (); HttpContext.Current.Response.SuppressContent = true; HttpContext.Current.ApplicationInstance.CompleteRequest ();
Nour Lababidi de

3
Não. Não funciona para mim. Na verdade, olhe para a resposta. Se Response.End()não não funciona, por que a resposta sugerida tem também Response.End()a última linha? Em vez disso, a resposta de @Binny (abaixo) ajuda!
user3454439

1
De acordo com a documentação em docs.microsoft.com/en-us/dotnet/api/system.web.httpresponse.end Request.End é suportado apenas para compatibilidade com versões anteriores. O uso de CompleteRequest é recomendado como substituição
Rudolf Dvoracek

11

Isso me ajudou a lidar com a Thread was being abortedexceção,

try
{
   //Write HTTP output
    HttpContext.Current.Response.Write(Data);
}  
catch (Exception exc) {}
finally {
   try 
    {
      //stop processing the script and return the current result
      HttpContext.Current.Response.End();
     } 
   catch (Exception ex) {} 
   finally {
        //Sends the response buffer
        HttpContext.Current.Response.Flush();
        // Prevents any other content from being sent to the browser
        HttpContext.Current.Response.SuppressContent = true;
        //Directs the thread to finish, bypassing additional processing
        HttpContext.Current.ApplicationInstance.CompleteRequest();
        //Suspends the current thread
        Thread.Sleep(1);
     }
   }

se você usar o seguinte código em vez de HttpContext.Current.Response.End(), obterá uma Server cannot append header after HTTP headers have been sentexceção.

            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.SuppressContent = True;
            HttpContext.Current.ApplicationInstance.CompleteRequest();

Espero que ajude


1
Funciona para mim. O acima não. Na verdade é engraçado que while Response.End()não funcione, mas o método sugerido também funcionou Response.End()na última linha?
user3454439

1
Porque você está capturando e ocultando a exceção.
Dan Friedman

3
Que solução horrível
Razor

4

Parece ser a mesma pergunta que:

Quando um ASP.NET System.Web.HttpResponse.End () é chamado, o segmento atual é abortado?

Portanto, é por design. Você precisa adicionar uma captura para essa exceção e "ignorá-la" normalmente.


Eu chamo essa função public void ExportDataSet (DataSet ds) em muitas telas aspx e também estou mantendo o método de logger de erros para exceções que são geradas em tempo de execução, essas exceções são gravadas em arquivos .txt. Portanto, essa mesma exceção é registrada em todos os arquivos txt da tela do aspx. Eu só quero evitar que essa exceção lance do arquivo de classe declarado do método para o aspx. Simplesmente, eu só quero lidar com essa exceção em meu próprio arquivo de classe de declaração de método.
user3171957

Por comentário do usuário em sua pergunta, basta pegar TheadAbortException -> catch (ThreadAbortException) {}
robnick

Sim, pegue essa exceção no próprio arquivo de classe de declaração do método.
user3171957

4

Mova o Response.End () para fora dos blocos Try / Catch e Using.

É suposto lançar uma exceção para ignorar o resto da solicitação, mas você não deveria capturá-la.

bool endRequest = false;

try
{
    .. do stuff
    endRequest = true;
}
catch {}

if (endRequest)
    Resonse.End();

por que não colocá-lo em um bloco finally, para que seja sempre executado?
GoldBishop

você pode fazer isso, especialmente se tiver uma instrução return no bloco try. Mas se você tentar / pegar / ignorar, você nem precisará do finally. o importante é que você não capture a ThreadAbortException.
Steve

Verdade, o TAE é um PITA para retornar uma resposta bem-sucedida.
GoldBishop

3

Basta colocar o

Response.End();

dentro de um bloco finally em vez de dentro do bloco try.

Isso tem funcionado para mim!!!.

Tive a seguinte estrutura de código problemática (com a exceção)

...
Response.Clear();
...
...
try{
 if (something){
   Reponse.Write(...);
   Response.End();

   return;

 } 

 some_more_code...

 Reponse.Write(...);
 Response.End();

}
catch(Exception){
}
finally{}

e lança a exceção. Suspeito que a exceção é lançada onde há código / trabalho a ser executado após response.End (); . No meu caso, o código extra era apenas o próprio retorno.

Quando acabei de mover a resposta.End (); para o bloco finally (e deixou o retorno em seu lugar - o que faz com que pular o resto do código no bloco try e saltar para o bloco finally (e não apenas sair da função que contém)) Exceção deixou de ter lugar.

O seguinte funciona bem:

...
Response.Clear();
...
...
try{
 if (something){
   Reponse.Write(...);

   return;

 } 

 some_more_code...

 Reponse.Write(...);

}
catch(Exception){
}
finally{
    Response.End();
}

3

Use um bloco catch especial para a exceção do método Response.End ()

{
    ...
    context.Response.End(); //always throws an exception

}
catch (ThreadAbortException e)
{
    //this is special for the Response.end exception
}
catch (Exception e)
{
     context.Response.ContentType = "text/plain";
     context.Response.Write(e.Message);
}

Ou apenas remova o Response.End () se estiver construindo um manipulador de arquivos



2

Eu removi o linkbutton do UpdatePanel e também comentei o Response.End () Success !!!


1

o erro para Response.END (); é porque você está usando um painel de atualização de asp ou qualquer controle que usa javascript, tente usar o controle nativo de asp ou html sem javascript ou scriptmanager ou script e tente novamente


1

Isso não é problema, mas é por design. A causa raiz está descrita na página de suporte da Microsoft.

O método Response.End termina a execução da página e muda a execução para o evento Application_EndRequest no pipeline de eventos do aplicativo. A linha de código que segue Response.End não é executada.

A solução fornecida é:

Para Response.End, chame o método HttpContext.Current.ApplicationInstance.CompleteRequest em vez de Response.End para ignorar a execução do código para o evento Application_EndRequest

Aqui está o link: https://support.microsoft.com/en-us/help/312629/prb-threadabortexception-occurs-if-you-use-response-end--response-redi


0

liberar a resposta para o cliente antes de response.end ()

Mais sobre o método Response.Flush

Portanto, use o código abaixo mencionado antes response.End();

response.Flush();  

0

Usei todas as alterações acima, mas ainda estava tendo o mesmo problema em meu aplicativo da web.

Então entrei em contato com meu provedor de hospedagem e pedi a eles para verificar se algum software ou antivírus bloqueava a transferência de nossos arquivos via HTTP. ou o ISP / rede não está permitindo a transferência do arquivo.

Eles verificaram as configurações do servidor e contornaram o "Firewall Compartilhado do Data Center" para o meu servidor e agora nosso aplicativo pode baixar o arquivo.

Espero que esta resposta ajude alguém. Isso é o que funcionou para mim


Embora possa funcionar, não parece uma solução sólida. Você está dizendo que o firewall está completamente desabilitado? Isso seria um grande "não". Ou é personalizado para o seu aplicativo? Também é estranho ver uma ThreadAbortException em algo que o firewall do datacenter bloqueia ... Em outras palavras, não é uma resposta para a pergunta?
Michael


0

Eu recomendo esta solução:

  1. Não use response.End();

  2. Declare esta var global: bool isFileDownLoad;

  3. Logo depois do seu (response.Write(sw.ToString());) set ==> isFileDownLoad = true;

  4. Substitua sua renderização como:

    /// AEG : Very important to handle the thread aborted exception
    
    override protected void Render(HtmlTextWriter w)
    {
         if (!isFileDownLoad) base.Render(w);
    } 

0

Descobri que o seguinte funcionou melhor ...

   private void EndResponse()
    {
        try
        {
            Context.Response.End();
        }
        catch (System.Threading.ThreadAbortException err)
        {
            System.Threading.Thread.ResetAbort();
        }
        catch (Exception err)
        {
        }
    }

0

Para mim, ajudou a registrar um botão que chama o código por trás do código como um controle de postback.

protected void Page_Init(object sender, EventArgs e)
{
    ScriptManager.GetCurrent(this.Page).RegisterPostBackControl(btnMyExport);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.