c# - ¿Por qué Response.Redirect causa System.Threading.ThreadAbortException?
asp.net .net-3.5 (10)
Cuando uso Response.Redirect (...) para redirigir mi formulario a una nueva página, aparece el error:
Se produjo una primera excepción de oportunidad del tipo ''System.Threading.ThreadAbortException'' en mscorlib.dll
Se produjo una excepción de tipo ''System.Threading.ThreadAbortException'' en mscorlib.dll pero no se manejó en el código de usuario
Mi entendimiento de esto es que el error está siendo causado por el servidor web que interrumpe el resto de la página en la que se llamó a response.redirect.
Sé que puedo agregar un segundo parámetro a Response.Redirect
que se llama endResponse. Si configuro endResponse en True, sigo recibiendo el error, pero si lo establezco en False, no lo hago. Sin embargo, estoy bastante seguro de que eso significa que el servidor web está ejecutando el resto de la página a la que redirigí. Lo que parece ser ineficiente por decir lo menos. ¿Hay una mejor manera de hacer esto? ¿Algo más que Response.Redirect
o hay una forma de forzar a la página antigua a que deje de cargar donde no se obtendrá una ThreadAbortException
?
Aquí está la línea oficial del problema (no pude encontrar la última, pero no creo que la situación haya cambiado para versiones posteriores de .net)
Así es como funciona Response.Redirect (url, true). Lanza la ThreadAbortException para abortar el hilo. Simplemente ignora esa excepción. (Supongo que es un controlador / registrador de errores global donde lo ves?)
Una discusión relacionada interesante ¿Es Response.End () considerado dañino?
El patrón correcto es llamar a la sobrecarga de redireccionamiento con endResponse = false y hacer una llamada para indicar a la canalización de IIS que debe avanzar directamente a la etapa EndRequest una vez que devuelva el control:
Response.Redirect(url, false);
Context.ApplicationInstance.CompleteRequest();
Esta publicación del blog de Thomas Marquardt proporciona detalles adicionales, incluido cómo manejar el caso especial de redireccionamiento dentro de un controlador Application_Error.
Incluso traté de evitar esto, solo en caso de realizar el aborto en el hilo manualmente, pero prefiero dejarlo con la "Solicitud completa" y seguir adelante: mi código tiene comandos de retorno después de redireccionar de todos modos. Así que esto se puede hacer
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();
}
Lo que hago es atrapar esta excepción, junto con otras posibles excepciones. Espero que esto ayude a alguien.
catch (ThreadAbortException ex1)
{
// do nothing
}
catch(Exception ex)
{
writeToLog(ex.Message);
}
No existe una solución simple y elegante para el problema de Redirect
en ASP.Net WebForms. Puedes elegir entre la solución sucia y la solución tediosa .
Dirty : Response.Redirect(url)
envía una redirección al navegador y luego lanza una ThreadAbortedException
para terminar el hilo actual. Por lo tanto, no se ejecuta ningún código más allá de la llamada Redirect (). Desventajas: es una mala práctica y tiene implicaciones de rendimiento para eliminar subprocesos como este. Además, ThreadAbortedExceptions
se mostrará en el registro de excepciones.
Tedioso : la forma recomendada es llamar a Response.Redirect(url, false)
y luego a Context.ApplicationInstance.CompleteRequest()
Sin embargo, la ejecución del código continuará y el resto de los controladores de eventos en el ciclo de vida de la página todavía se ejecutarán. (Por ejemplo, si realiza la redirección en Page_Load, no solo se ejecutará el resto del controlador, también se llamará a Page_PreRender, etc., la página representada simplemente no se enviará al navegador. Puede evitar el procesamiento adicional por ejemplo, establecer un indicador en la página y luego permitir que los controladores de eventos subsiguientes verifiquen este indicador antes de realizar cualquier procesamiento.
(La documentación de CompleteRequest
indica que "hace que ASP.NET omita todos los eventos y el filtrado en la cadena de ejecución de la canalización HTTP ". Esto se puede malinterpretar fácilmente. Se omiten más filtros y módulos HTTP, pero no se omite más eventos en el ciclo de vida de la página actual.)
El problema más profundo es que WebForms carece de un nivel de abstracción. Cuando se encuentra en un controlador de eventos, ya está en el proceso de crear una página para generar resultados. La redirección en un controlador de eventos es fea porque está terminando una página parcialmente generada para generar una página diferente. MVC no tiene este problema ya que el flujo de control es independiente de las vistas de representación, por lo que puede hacer una redirección limpia simplemente devolviendo una RedirectAction
en el controlador, sin generar una vista.
Sé que llego tarde, pero solo he tenido este error si mi Response.Redirect
está en un bloque Try...Catch
.
Nunca coloque un Response.Redirect en un bloque Try ... Catch. Es mala practica
Editar
En respuesta al comentario de @Kiquenet, esto es lo que haría como alternativa a poner el Response.Redirect en el bloque Try ... Catch.
Yo dividiría el método / función en dos pasos.
El primer paso dentro del bloque Try ... Catch realiza las acciones solicitadas y establece un valor de "resultado" para indicar el éxito o el fracaso de las acciones.
Paso dos fuera del bloque Try ... Catch realiza la redirección (o no lo hace) dependiendo de cuál es el valor del "resultado".
Este código está lejos de ser perfecto y probablemente no debería copiarse ya que no lo he probado
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.
}
}
También probé otra solución, pero parte del código ejecutado después de la redirección.
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();
}
Así que si hay que evitar la ejecución de código después de la redirección
try
{
//other code
Response.Redirect("")
// code not to be executed
}
catch(ThreadAbortException){}//do there id nothing here
catch(Exception ex)
{
//Logging
}
Yo también tuve ese problema. Trate de usar Server.Transfer en lugar de Response.Redirect Trabajó para mí
Response.Redirect()
lanza una excepción para abortar la solicitud actual.
Este artículo de KB describe este comportamiento (también para los métodos Request.End()
y Server.Transfer()
).
Para Response.Redirect()
existe una sobrecarga:
Response.Redirect(String url, bool endResponse)
Si pasa endResponse = false , la excepción no se produce (pero el tiempo de ejecución continuará procesando la solicitud actual).
Si endResponse = true (o si se usa la otra sobrecarga), se lanza la excepción y la solicitud actual terminará de inmediato.