Response.End () é considerado prejudicial?


198

Este artigo da Response.End()Base de dados de KB diz que o ASP.NET interrompe um thread.

Refletor mostra que ele se parece com isso:

public void End()
{
    if (this._context.IsInCancellablePeriod)
    {
        InternalSecurityPermissions.ControlThread.Assert();
        Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
    }
    else if (!this._flushing)
    {
        this.Flush();
        this._ended = true;
        if (this._context.ApplicationInstance != null)
        {
            this._context.ApplicationInstance.CompleteRequest();
        }
    }
}

Isso parece muito duro para mim. Como o artigo da KB diz, qualquer código no aplicativo a seguir Response.End()não será executado e isso viola o princípio de menor espanto. É quase como Application.Exit()em um aplicativo WinForms. A exceção de cancelamento de encadeamento causada por Response.End()não é capturável, portanto, cercar o código em try... finallynão será satisfatório.

Isso me faz pensar se devo sempre evitar Response.End().

Alguém pode sugerir, quando devo usar Response.End(), quando Response.Close()e quando HttpContext.Current.ApplicationInstance.CompleteRequest()?

ref: entrada no blog de Rick Strahl .


Com base nas informações recebidas, minha resposta é: Sim, Response.Endé prejudicial , mas é útil em alguns casos limitados.

  • use Response.End()como um lançamento inalcançável, para encerrar imediatamente o HttpResponseem condições excepcionais. Também pode ser útil durante a depuração. Evite Response.End()completar respostas de rotina .
  • use Response.Close()para fechar imediatamente a conexão com o cliente. Por esta postagem do blog do MSDN , esse método não se destina ao processamento normal de solicitações HTTP. É altamente improvável que você tenha um bom motivo para chamar esse método.
  • use CompleteRequest()para finalizar uma solicitação normal. CompleteRequestfaz com que o pipeline do ASP.NET pule para o EndRequestevento, após a conclusão do HttpApplicationevento atual . Portanto, se você ligar CompleteRequest, escreva algo mais para a resposta, a gravação será enviada ao cliente.

Editar - 13 de abril de 2011

Mais clareza está disponível aqui:
- Postagem útil no Blog do MSDN
- Análise útil por Jon Reid


2
não faço ideia do que mudou desde essa resposta, mas estou percebendo o que há de Response.End ThreadAbortExceptionbom.
— Maslow

1
Tenha também em mente que, Response.Redirecte Server.Transferambos chamam Response.Ende também devem ser evitados.
— quer

Respostas:


66

Se você tiver empregado um criador de exceções no seu aplicativo, ele será diluído com os ThreadAbortExceptions dessas Response.End()chamadas benignas . Eu acho que essa é a maneira da Microsoft dizer "Pare com isso!".

Eu usaria apenas Response.End()se houvesse alguma condição excepcional e nenhuma outra ação fosse possível. Talvez então, o registro dessa exceção possa realmente indicar um aviso.


107

TL; DR

Inicialmente, eu havia recomendado que você simplesmente substituísse todas as suas chamadas para [Response.End] por [...] CompleteRequest (), mas se você quiser evitar o processamento de postback e a renderização em html, precisará adicionar [..] .] substitui também.

Jon Reid , "Análise Final"


Por MSDN, Jon Reid e Alain Renon:

Desempenho do ASP.NET - Gerenciamento de exceções - Escreva código que evite exceções

Os métodos Server.Transfer, Response.Redirect, Response.End todos geram exceções. Cada um desses métodos chama internamente Response.End. A chamada para Response.End, por sua vez, causa uma exceção ThreadAbortException .

Solução ThreadAbortException

HttpApplication.CompleteRequest () define uma variável que faz com que o thread ignore a maioria dos eventos no pipeline de eventos HttpApplication [-] não na cadeia de eventos Page, mas na cadeia de eventos Application.

...

crie uma variável em nível de classe que sinalize se a página deve terminar e verifique a variável antes de processar seus eventos ou renderizar sua página. [...] eu recomendaria apenas substituir os métodos RaisePostBackEvent e Render

Response.End e Response.Close não são usados ​​no processamento normal de solicitações quando o desempenho é importante. Response.End é um meio conveniente e pesado de encerrar o processamento de solicitações com uma penalidade de desempenho associada. Response.Close é para o encerramento imediato da resposta HTTP no nível do IIS / soquete e causa problemas com coisas como KeepAlive.

O método recomendado para finalizar uma solicitação do ASP.NET é HttpApplication.CompleteRequest. Lembre-se de que a renderização do ASP.NET precisará ser ignorada manualmente, pois o HttpApplication.CompleteRequest ignora o restante do pipeline do aplicativo IIS / ASP.NET, não o pipeline da página ASP.NET (que é um estágio do pipeline do aplicativo).


Código

Copyright © 2001-2007, C6 Software, Inc da melhor forma possível.


Referência

HttpApplication.CompleteRequest

Faz com que o ASP.NET ignore todos os eventos e filtragem na cadeia de execução do pipeline HTTP e execute diretamente o evento EndRequest.

Response.End

Este método é fornecido apenas para compatibilidade com o ASP - ou seja, para compatibilidade com a tecnologia de programação da Web baseada em COM que precedeu o ASP.NET precedido pelo ASP.NET. [Enfase adicionada]

Response.Close

Este método finaliza a conexão com o cliente de maneira abrupta e não se destina ao processamento normal de solicitações HTTP . [Enfase adicionada]


4
> Lembre-se de que a renderização do ASP.NET terá que ser ignorada manualmente, pois o HttpApplication.CompleteRequest ignora o restante do pipeline de aplicativos IIS / ASP.NET, não o pipeline de página ASP.NET (que é um estágio do pipeline de aplicativos). E como você consegue isso?
— PilotBob 18/10/12

1
Veja o link para o código em que Jon Reid demonstrou como definir um sinalizador e substituir os métodos RaisePostBackEvent e Render da página para ignorar a implementação normal quando desejado. (Você provavelmente faria isso em uma classe base da qual todas as páginas do seu aplicativo herdarão.) Web.archive.org/web/20101224113858/http://www.c6software.com/…
— user423430

4
Apenas para reafirmar: HttpApplication.CompleteRequest não finaliza a resposta como Response.End.
— Glen Little

2
HttpApplication.CompleteRequest também não interrompe o fluxo de código, portanto, as linhas subsequentes continuam em execução. Isso pode não afetar o que o navegador vê, mas se essas linhas fizerem outro processamento, pode ser realmente confuso.
— Joshua Frank

3
Não consigo pensar, mas o Web Forms está quebrado por design. O que é mais degradante no desempenho, chamando Response.End () ou deixa a página carregar tudo e suprime a resposta? Não consigo ver onde Response.End () é "mais" prejudicial aqui. Além disso, a Microsoft trata o `ThreadAbortedException 'como um evento normal, como é evidente neste código: referencesource.microsoft.com/#System.Web/UI/Page.cs,4875 Uma coisa que é contra Response.End () é que ele pode falhar abortar a resposta, o que pode resultar ocasionalmente na resposta sendo exibida.
— Ghasan

99

Esta pergunta aparece no topo de todas as pesquisas do Google por informações sobre response.end, assim, para outras pesquisas como eu que desejam postar CSV / XML / PDF etc. em resposta a um evento sem renderizar a página ASPX inteira, é assim que eu faço . (substituir os métodos de renderização é excessivamente complexo para uma tarefa tão simples IMO)

// Add headers for a csv file or whatever
Response.ContentType = "text/csv"
Response.AddHeader("Content-Disposition", "attachment;filename=report.csv")
Response.AddHeader("Pragma", "no-cache")
Response.AddHeader("Cache-Control", "no-cache")

// Write the data as binary from a unicode string
Dim buffer As Byte()
buffer = System.Text.Encoding.Unicode.GetBytes(csv)
Response.BinaryWrite(buffer)

// Sends the response buffer
Response.Flush()

// Prevents any other content from being sent to the browser
Response.SuppressContent = True

// Directs the thread to finish, bypassing additional processing
HttpContext.Current.ApplicationInstance.CompleteRequest()

1
Você não deveria estar usando uma página APSX para fazer isso. É muito esforço desperdiçado. Você deve usar um ASMX ou serviço da Web, tudo menos uma página ASPX.
— mattmanser

19
Essa parece ser a resposta com a implementação mais fácil. A chave é Response.SuppressContent = True.
— 31813 Chris Weber

3
@mattmanser - Nem sempre é fácil / melhor / aconselhável ter uma página separada para representação diferente do mesmo recurso. Pense em REST, etc. Se o cliente indicar que deseja csv, xml por meio de um cabeçalho ou parâmetro, esse método certamente seria o melhor, enquanto ainda fornece suporte a html através dos recursos normais de renderização do asp.net.
— 31813 Chris Weber

1
Isso não funcionou para mim. Eu tinha uma página que funcionava com Response.End (), mas usando todos os tipos de combinações de Response.Close (), Response.Flush (). HttpContext.Current.ApplicationInstance.CompleteRequest () e várias outras coisas não funcionariam se eu tivesse um filtro GzipStream na resposta. O que parecia estar acontecendo era que a página ainda estava sendo impressa junto com o meu arquivo. Finalmente, substituí a função Render () (em branco) e isso a resolveu para mim.
— Aerik 7/08/14

1
O CompleteRequest ignora partes do pipeline do aplicativo, mas ainda é executado no restante do processo de renderização da página, não é uma parada imediata como response.end, é mais elegante. Há explicações mais detalhadas sobre o motivo em outras respostas nesta página.
— Jay Zelos

11

Na questão de "Ainda não sei a diferença entre Response.Close e CompleteRequest ()", eu diria:

Prefere CompleteRequest (), não use Response.Close ().

Veja o seguinte artigo para obter um resumo bem feito deste caso.

Lembre-se de que, mesmo depois de chamar CompleteRequest (), algum texto (por exemplo, redndered do código ASPX) seria anexado ao fluxo de saída da resposta. Você pode evitá-lo, substituindo os métodos Render e RaisePostBackEvent, conforme descrito no artigo a seguir .

BTW: Concordo em impedir o uso de Response.End (), especialmente ao gravar dados no fluxo http para emular o download do arquivo. Usamos o Response.End () no passado até nosso arquivo de log ficar cheio de ThreadAbortExceptions.


Estou interessado em substituir a renderização conforme você descreve, mas o link para o "artigo a seguir" está inoperante. Talvez você possa atualizar sua entrada?
— 22414 paqogomez

Desculpe por uma resposta tardia. Não me lembro exatamente o que estava naquele artigo. No entanto, eu encontrei-o no webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/...
— Jan Sotola

9

Não concordo com a afirmação " Response.End é prejudicial ". Definitivamente, não é prejudicial. Response.End faz o que diz; termina a execução da página. Usar o refletor para ver como foi implementado deve ser visto apenas como instrutivo.


Minha recomendação 2cent
EVITAR usando Response.End()como controle fluxo.
DO usoResponse.End() se você precisa parar a execução do pedido e estar ciente de que (geralmente) * nenhum código será executado além desse ponto.


* Response.End()e ThreadAbortException s.

Response.End() lança um ThreadAbortException como parte de sua implementação atual (conforme observado pelo OP).

ThreadAbortException é uma exceção especial que pode ser capturada, mas será automaticamente gerada novamente no final do bloco catch.

Para ver como escrever código que deve lidar com ThreadAbortExceptions, consulte a resposta de @ Mehrdad para SO. Como posso detectar uma threadabortexception em um bloco finalmente onde ele faz referência ao método RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup e regiões de execução restrita


O artigo de Rick Strahl mencionado é instrutivo e certifique-se de ler os comentários também. Observe que o problema de Strahl era específico. Ele queria levar os dados para o cliente (uma imagem) e depois processar a atualização do banco de dados de rastreamento de ocorrências que não atrasava a veiculação da imagem, o que tornava o problema de fazer alguma coisa após o Response.End ter sido chamado.


Vimos este post stackoverflow.com/questions/16731745/... sugerindo usando Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () em vez de Response.End ()
— jazzBox

3

Eu nunca considerei usar Response.End () para controlar o fluxo do programa.

No entanto, Response.End () pode ser útil, por exemplo, ao exibir arquivos para um usuário.

Você gravou o arquivo na resposta e não deseja que mais nada seja adicionado à resposta, pois isso pode corromper o arquivo.


1
Entendo a necessidade de uma API dizer "a resposta está completa". Mas Response.End () também anula um encadeamento. Este é o cerne da questão. Quando é uma boa ideia juntar essas duas coisas?
— Cheeso

2

Eu usei Response.End () no .NET e no ASP clássico para finalizar com força antes. Por exemplo, eu o uso quando há uma quantidade certian de tentativas de login. Ou quando uma página segura está sendo acessada a partir de um login não autenticado (exemplo aproximado):

    if (userName == "")
    {
        Response.Redirect("......");
        Response.End();
    }
    else
    {
      .....

Ao veicular arquivos para um usuário que eu usaria um Flush, o final pode causar problemas.


Lembre-se de que Flush () não é "esse é o fim". É apenas "liberar tudo até agora". O motivo para você querer "este é o fim" é deixar o cliente ciente de que possui todo o conteúdo, enquanto o servidor pode fazer outras coisas - atualizar um arquivo de log, consultar um contador de banco de dados ou qualquer outra coisa. Se você chamar Response.Flush e executar uma dessas ações, o cliente poderá continuar aguardando mais. Se você chamar Response.End () então o controle salta para fora e não o DB não se consultas, etc.
— Cheeso

Você pode alternativamente usar a Response.Redirect override (" .... " true), onde o bool é 'endResponse: Indica se a execução atual da página deve terminar"
— Robert Paulson

É sempre melhor usar a estrutura de autenticação de formulários para proteger as páginas que devem ser protegidas por credenciais de login.
— Robert Paulson

3
Na verdade, para me corrigir, acredito que o padrão Response.Redirect e Server.Transfer devem chamar Response.End internamente, a menos que você chame a substituição e passe 'false'. A maneira como seu código é escrito Response.End nunca é chamado ,
— Robert Paulson

1
O Response.end funciona de maneira muito diferente no .net do que no ASP clássico. No .net, ele causa uma threadabortexception, que pode ser bastante desagradável.
— Andy

2

Eu apenas usei Response.End () como um mecanismo de teste / depuração

<snip>
Response.Write("myVariable: " + myVariable.ToString());
Response.End();
<snip>

A julgar pelo que você publicou em termos de pesquisa, eu diria que seria um projeto ruim se exigisse Response.End


0

No asp clássico, eu tinha um TTFB (Time To First Byte) de 3 a 10 segundos em algumas chamadas ajax, muito maior que o TTFB em páginas regulares com muito mais chamadas SQL.

O ajax retornado era um segmento de HTML a ser injetado na página.

O TTFB demorou vários segundos a mais que o tempo de renderização.

Se eu adicionasse um response.end após a renderização, o TTFB seria bastante reduzido.

Eu poderia obter o mesmo efeito emitindo um "</body> </html>", mas isso provavelmente não funciona ao gerar json ou xml; aqui response.end é necessário.


Eu sei que essa é uma resposta antiga, mas, novamente, o asp clássico é antigo. Achei que seria útil ;-)
— Leif Neland
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.