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

Cheeso
fonte
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.

Spoulson
fonte
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]

user423430
fonte
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()
Jay Zelos
fonte
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.

Jan Šotola
fonte
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.

Robert Paulson
fonte
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.

Bolo de peixe
fonte
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.

Tim Meers
fonte
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

Nathan Koop
fonte
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.

Leif Neland
fonte
Eu sei que essa é uma resposta antiga, mas, novamente, o asp clássico é antigo. Achei que seria útil ;-)
Leif Neland