¿Response.End () se considera dañino?

198

Este artículo de KB dice que ASP.NET Response.End()aborta un hilo.

Reflector muestra que se ve así:

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

Esto me parece bastante duro. Como dice el artículo de KB, Response.End()no se ejecutará ningún código en la aplicación siguiente , y eso viola el principio de mínimo asombro. Es casi como Application.Exit()en una aplicación WinForms. El hilo de aborto causado por la excepción Response.End()no es capturable, por lo que rodea el código en un try... finallyno va a satisfacer.

Me hace preguntarme si siempre debería evitarlo Response.End().

¿Alguien puede sugerir cuándo debo usar Response.End()cuándo Response.Close()y cuándo HttpContext.Current.ApplicationInstance.CompleteRequest()?

ref: entrada del blog de Rick Strahl .


Según la información que he recibido, mi respuesta es Sí, Response.Endes dañino , pero es útil en algunos casos limitados.

  • utilícelo Response.End()como un tiro inalcanzable, para terminar inmediatamente HttpResponseen condiciones excepcionales. Puede ser útil durante la depuración también. Evite Response.End()completar respuestas de rutina .
  • use Response.Close()para cerrar inmediatamente la conexión con el cliente. Según esta publicación de blog de MSDN , este método no está destinado al procesamiento normal de solicitudes HTTP. Es muy poco probable que tenga una buena razón para llamar a este método.
  • use CompleteRequest()para finalizar una solicitud normal. CompleteRequesthace que la canalización de ASP.NET salte al EndRequestevento una vez HttpApplicationque se complete el evento actual . Entonces, si llama CompleteRequesty luego escribe algo más en la respuesta, la escritura se enviará al cliente.

Editar - 13 de abril de 2011

Más claridad está disponible aquí:
- Publicación útil en el blog de MSDN
- Análisis útil de Jon Reid

Cheeso
fuente
2
No tengo idea de qué ha cambiado desde esta respuesta, pero estoy entendiendo Response.End ThreadAbortExceptionmuy bien.
Maslow
1
Tenga en cuenta también eso Response.Redirecty Server.Transferambos llaman Response.Endy también deben evitarse.
Owen Blacker el

Respuestas:

66

Si ha empleado un registrador de excepciones en su aplicación, se diluirá con los ThreadAbortExceptions de estas Response.End()llamadas benignas . Creo que esta es la forma en que Microsoft dice "¡Ya basta!".

Solo lo usaría Response.End()si hubiera alguna condición excepcional y no fuera posible realizar ninguna otra acción. Quizás entonces, registrar esta excepción podría indicar una advertencia.

Spoulson
fuente
107

TL; DR

Inicialmente, le había recomendado que simplemente reemplazara todas sus llamadas a [Response.End] con [...] llamadas CompleteRequest (), pero si desea evitar el procesamiento de devolución y la representación html, deberá agregar [..] .] anula también.

Jon Reid , "Análisis final"


Por MSDN, Jon Reid y Alain Renon:

Rendimiento de ASP.NET - Gestión de excepciones - Escribir código que evite excepciones

Los métodos Server.Transfer, Response.Redirect, Response.End generan excepciones. Cada uno de estos métodos llama internamente Response.End. La llamada a Response.End, a su vez, provoca una excepción ThreadAbortException .

Solución ThreadAbortException

HttpApplication.CompleteRequest () establece una variable que hace que el subproceso omita la mayoría de los eventos en la canalización de eventos HttpApplication [-] no la cadena de eventos de Página sino la cadena de eventos de Aplicación.

...

cree una variable de nivel de clase que marque si la página debe terminar y luego verifique la variable antes de procesar sus eventos o mostrar su página. [...] Recomendaría simplemente anular los métodos RaisePostBackEvent y Render

Response.End y Response.Close no se utilizan en el procesamiento normal de solicitudes cuando el rendimiento es importante. Response.End es un medio conveniente y pesado para finalizar el procesamiento de solicitudes con una penalización de rendimiento asociada. Response.Close es para la terminación inmediata de la respuesta HTTP en el nivel IIS / socket y causa problemas con cosas como KeepAlive.

El método recomendado para finalizar una solicitud ASP.NET es HttpApplication.CompleteRequest. Tenga en cuenta que la representación de ASP.NET deberá omitirse manualmente ya que HttpApplication.CompleteRequest omite el resto de la canalización de la aplicación IIS / ASP.NET, no la canalización de la página ASP.NET (que es una etapa en la canalización de la aplicación).


Código

Copyright © 2001-2007, C6 Software, Inc lo mejor que pude ver.


Referencia

HttpApplication.CompleteRequest

Hace que ASP.NET omita todos los eventos y el filtrado en la cadena de ejecución de la canalización HTTP y ejecute directamente el evento EndRequest.

Respuesta Fin

Este método se proporciona solo para la compatibilidad con ASP , es decir, para la compatibilidad con la tecnología de programación web basada en COM que precedió a ASP.NET. [Énfasis añadido]

Respuesta Cerrar

Este método finaliza la conexión con el cliente de manera abrupta y no está destinado al procesamiento normal de solicitudes HTTP . [Énfasis añadido]

usuario423430
fuente
44
> Tenga en cuenta que la representación de ASP.NET deberá omitirse manualmente ya que HttpApplication.CompleteRequest omite el resto de la canalización de la aplicación IIS / ASP.NET, no la canalización de la página ASP.NET (que es una etapa en la canalización de la aplicación). ¿Y cómo logras esto?
PilotBob
1
Vea el enlace al código donde Jon Reid demostró cómo establecer un indicador y anular los métodos RaisePostBackEvent y Render de la página para omitir la implementación normal cuando lo desee. (Lo que probablemente hace esto en una clase base todas las páginas de tu aplicación deben heredar de.) Web.archive.org/web/20101224113858/http://www.c6software.com/...
user423430
44
Solo para reformular: HttpApplication.CompleteRequest no termina la respuesta como Response.End sí.
Glen Little
2
HttpApplication.CompleteRequest tampoco detiene el flujo de código, por lo que las líneas posteriores siguen ejecutándose. Es posible que eso no afecte lo que ve el navegador, pero si esas líneas realizan algún otro procesamiento, puede ser realmente confuso.
Joshua Frank
3
No puedo pensar que Web Forms está roto por diseño. ¿Qué es más degradante en el rendimiento, llamando a Response.End () o dejando que la página cargue todo y luego suprima la respuesta? No puedo ver dónde Response.End () es "más" dañino aquí. Además, Microsoft trata a 'ThreadAbortedException' como un evento normal como lo demuestra este código: referencesource.microsoft.com/#System.Web/UI/Page.cs,4875 Una cosa que está en contra de Response.End () es que podría fallar abortando la respuesta, lo que podría ocasionalmente mostrar la respuesta.
Ghasan
99

Esta pregunta aparece cerca de la parte superior de todas las búsquedas de Google para obtener información sobre la respuesta. Para otras búsquedas como yo que desean publicar CSV / XML / PDF, etc. en respuesta a un evento sin mostrar toda la página ASPX, así es como lo hago. . (anular los métodos de renderizado es demasiado complejo para una tarea tan simple como OMI)

// 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
fuente
1
No deberías estar usando una página APSX para hacer esto. Es mucho esfuerzo desperdiciado. Se supone que debe usar un ASMX o un servicio web, cualquier cosa menos una página ASPX.
Mattmanser
19
Esta parece ser la respuesta con la implementación más fácil. La clave es Response.SuppressContent = True.
Chris Weber
3
@mattmanser: no siempre es fácil / mejor / aconsejable tener una página separada para una representación diferente del mismo recurso. Piense en REST, etc. Si el cliente indica que quiere csv, xml a través de un encabezado o parámetro, este método sin duda sería el mejor, a la vez que proporciona soporte html a través de las instalaciones de renderización normales de asp.net.
Chris Weber
1
Esto no funcionó para mí. Tenía una página que funcionaba con Response.End (), pero con todo tipo de combinaciones de Response.Close (), Response.Flush (). HttpContext.Current.ApplicationInstance.CompleteRequest () y varias otras cosas no funcionaron si tuviera un filtro GzipStream en la respuesta. Lo que parecía estar sucediendo era que la página todavía se estaba imprimiendo junto con mi archivo. Finalmente anulé la función Render () (estar en blanco) y eso me resolvió.
Aerik
1
CompleteRequest omite partes de la canalización de la aplicación, pero aún se ejecutará durante el resto del proceso de representación de la página, no es una parada inmediata como respuesta, y es más elegante. Hay explicaciones más detalladas de por qué en otras respuestas en esta página.
Jay Zelos
11

Sobre la pregunta "Todavía no sé la diferencia entre Response.Close y CompleteRequest ()", diría:

Prefiere CompleteRequest (), no use Response.Close ().

Consulte el siguiente artículo para obtener un resumen bien hecho de este caso.

Tenga en cuenta que incluso después de llamar a CompleteRequest (), se agregará texto (por ejemplo, redirigido del código ASPX) a la secuencia de salida de respuesta. Puede evitarlo anulando los métodos Render y RaisePostBackEvent como se describe en el siguiente artículo .

Por cierto: estoy de acuerdo con evitar el uso de Response.End (), especialmente al escribir datos en la secuencia http para emular la descarga de archivos. Hemos utilizado Response.End () en el pasado hasta que nuestro archivo de registro se llenó de ThreadAbortExceptions.

Jan Šotola
fuente
Estoy interesado en anular Render como lo describe, pero el enlace al "siguiente artículo" está muerto. ¿Quizás pueda actualizar su entrada?
paqogomez
Perdón por una respuesta tardía. No recuerdo exactamente qué había en ese artículo. Sin embargo, lo encontré en webarchive.org: web.archive.org/web/20101224113858/http://www.c6software.com/…
Jan Šotola
9

No estoy de acuerdo con la afirmación " Response.End es perjudicial ". Definitivamente no es dañino. Respuesta. Fin hace lo que dice; finaliza la ejecución de la página. El uso del reflector para ver cómo se implementó solo debe verse como instructivo.


Mi recomendación de 2cent
EVITE usarla Response.End()como flujo de control.
DO uso Response.End()si es necesario para detener la ejecución petición y ser consciente de que (por lo general) * sin código se ejecutará allá de ese punto.


* Response.End()y ThreadAbortException s.

Response.End() lanza una ThreadAbortException como parte de su implementación actual (como lo señala OP).

ThreadAbortException es una excepción especial que se puede detectar, pero se volverá a generar automáticamente al final del bloque catch.

Para ver cómo escribir código que debe tratar con ThreadAbortExceptions, consulte la respuesta de @ Mehrdad a SO ¿Cómo puedo detectar una excepción de amenaza de amenaza en un bloque finalmente donde hace referencia a RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup Method y Restrained Execution Regions?


El artículo de Rick Strahl mencionado es instructivo, y asegúrese de leer los comentarios también. Tenga en cuenta que el problema de Strahl fue específico. Quería llevar los datos al cliente (una imagen) y luego procesar la actualización de la base de datos de seguimiento de visitas que no ralentizara el servicio de la imagen, lo que hizo que su problema fuera hacer algo después de la respuesta.

Robert Paulson
fuente
Vimos esta publicación stackoverflow.com/questions/16731745/… sugiriendo usar Response.SuppressContent = True HttpContext.Current.ApplicationInstance.CompleteRequest () en lugar de Response.End ()
jazzBox
3

Nunca he considerado usar Response.End () para controlar el flujo del programa.

Sin embargo, Response.End () puede ser útil, por ejemplo, cuando sirve archivos a un usuario.

Ha escrito el archivo en la respuesta y no desea que se agregue nada más a la respuesta, ya que puede dañar su archivo.

Pastel de pescado
fuente
1
Entiendo la necesidad de una API que diga "la respuesta está completa". Pero Response.End () también aborta un hilo. Este es el quid de la pregunta. ¿Cuándo es una buena idea unir esas dos cosas?
Cheeso el
2

He usado Response.End () tanto en .NET como en ASP clásico para terminar con fuerza las cosas antes. Por ejemplo, lo uso cuando hay una cierta cantidad de intentos de inicio de sesión. O cuando se accede a una página segura desde un inicio de sesión no autenticado (ejemplo aproximado):

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

Al entregar archivos a un usuario, usaría un Flush, el Fin puede causar problemas.

Tim Meers
fuente
Tenga en cuenta que Flush () no es "este es el final". Es solo "tirar todo hasta ahora". La razón por la que puede querer un "este es el final" es para que el cliente sepa que tiene todo el contenido, mientras que el servidor puede ir y hacer otras cosas: actualizar un archivo de registro, consultar un contador de base de datos o lo que sea. Si llama a Response.Flush y luego hace una de esas cosas, el cliente puede continuar esperando más. Si llama Response.End () entonces el control salta a la vista y el PP no recibe consultas, etc.
Cheeso
Alternativamente, podría utilizar la respuesta de anulación Response.Redirect ("....", verdadero) donde el bool es 'endResponse: indica si la ejecución actual de la página debe terminar "
Robert Paulson
Siempre es mejor usar el marco de autenticación de formularios para proteger las páginas que deben protegerse con credenciales de inicio de sesión.
Robert Paulson
3
En realidad, para corregirme, creo que el valor predeterminado de Response.Redirect y Server.Transfer es llamar a Response.End internamente a menos que llame a la anulación y pase 'false'. De la forma en que se escribe su código, Response.End nunca se llama ,
Robert Paulson
1
Response.end funciona de manera muy diferente en .net que en ASP clásico. En .net causa una amenaza de bortexcepción, que puede ser bastante desagradable.
Andy
2

Solo he usado Response.End () como mecanismo de prueba / depuración

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

A juzgar por lo que ha publicado en términos de investigación, diría que sería un mal diseño si requiriera respuesta.

Nathan Koop
fuente
0

En asp clásico, tenía un TTFB (Time To First Byte) de 3 a 10 segundos en algunas llamadas ajax, mucho más grande que el TTFB en páginas normales con muchas más llamadas SQL.

El ajax devuelto fue un segmento de HTML para ser inyectado en la página.

El TTFB fue varios segundos más largo que el tiempo de renderizado.

Si agregué una respuesta al final del render, el TTFB se redujo considerablemente.

Podría obtener el mismo efecto emitiendo un "</body> </html>", pero esto probablemente no funcione al generar json o xml; aquí se necesita respuesta.end.

Leif Neland
fuente
Sé que esta es una respuesta antigua, pero, de nuevo, el asp clásico es viejo. Lo encontré útil ;-)
Leif Neland