asp.net - Forma correcta de omitir la ejecución de la página después de Response.RedirectToRoute
vb.net webforms (2)
Estoy escribiendo una aplicación asp.net 4.5 usando las nuevas características de enrutamiento. Tengo una página que muestra información sobre un artículo. En el evento Page_Load
los datos de la ruta (identificación del artículo) y los permisos del usuario, y si algo no está bien (por ejemplo, la identificación es para un artículo eliminado) utilizo Response.RedirectToRoute
para enviarlos a empaquetar, de regreso a la página de inicio . No pase GO, no cobra $ 200.
Esto tiene mucho sentido hasta que intenté acceder a un elemento eliminado y en lugar de la página de inicio, recibí una página de error. Hice algunas excavaciones y descubrí que incluso después de usar RedirectToRoute
(a diferencia del método Redirect
estándar) el resto del código de la página continúa ejecutándose , lo que al menos parece un desperdicio (ya que voy a descartar los resultados) y arroja errores cuando los datos necesarios no existen.
Hice un poco más de SO mining y descubrí el increíble mal que es Response.End()
. Hace lo que necesito, pero incluso la página de MSDN me dice que Response.End
es el hijo bastardo de un antiguo lenguaje maldito y no es apto para ver la luz del día. La principal objeción parece ser el hecho de que Response.End arroja una excepción, y eso es malo para el rendimiento. No soy el desarrollador más experimentado, así que no entiendo el problema por completo, pero me cuesta creer que lanzar una excepción sea más costoso que cargar toda la página web. Las soluciones parecen bastante complejas y excesivas para una tarea tan simple, especialmente dado que la mayoría de las páginas requieren algún tipo de control de validez.
¿Qué se supone que debo hacer en esta situación? Use Response.End
y pida perdón por mi insolencia? ¿Cobble juntos alguna solución fea? ¿O mi perspectiva sobre el problema es incorrecta para empezar? Realmente me gustaría saber
Actualización: ahora que lo he pensado un poco más, me pregunto si tengo una perspectiva equivocada del problema. Tal vez una redirección inmediata no sea la mejor respuesta para la experiencia del usuario. ¿Sería mejor que envuelva todos los controles en un panel y use algo como esto?
Private Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
''Validation Code
If notValid Then
ControlsPanel.Visible = false
ErrorPanel.Visible = true
End If
End Sub
RedirectToRoute en realidad envuelve Response.Redirect y pasa false
para finalizar la solicitud; por lo tanto, la solicitud continúa. Puede utilizar HttpApplication.CompleteRequest como llamada inmediata para finalizar la solicitud para que no se invoquen los siguientes eventos de la aplicación.
Response.End
(y otra variación de redirección) lanza ThreadAbortException
para abortar el proceso de solicitud de subproceso, que es realmente una mala manera de detener el procesamiento de solicitud. En .NET world, el procesamiento de excepciones siempre se considera costoso porque CLR necesita buscar la pila hasta arriba para los bloques de procesamiento de excepciones, crear el seguimiento de pila, etc. IMO, CompleteRequest
se introdujo en .NET 1.1 para evitar lo mismo que realmente depende en la configuración del indicador en el código de la infraestructura ASP.NET para omitir el procesamiento posterior, excepto el evento EndRequest
.
Otra forma (y mejor) es utilizar Server.Transfer y evitar el viaje de ida y vuelta del cliente para establecer la redirección. El único problema es que el cliente no vería la URL redirigida en la barra de direcciones del navegador. Por lo general, prefiero este método.
EDITAR
CompleteRequest
nunca funcionaría en el caso de la página donde los eventos de página subsecuentes se seguirían invocando porque la página es un manejador, todos sus eventos ocurren dentro de un único evento de aplicación (y actual) ProcessRequest
. Por lo tanto, la única forma de establecer una bandera es verificarla en anulaciones como Render
, PreRender
, RaisePostBackEvent
, etc.
Desde la perspectiva del mantenimiento, tiene sentido tener dicha funcionalidad en la clase de página base (es decir, mantener el indicador, ofrecer el método CompleteRequest
a las subclases y anular los métodos de eventos del ciclo de vida). Por ejemplo,
internal class PageBase: System.Web.UI.Page
{
bool _requestCompleted;
protected void CompleteRequest()
{
Context.ApplicationInstance.CompleteRequest();
_requestCompleted = true;
}
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl,
string eventArgument)
{
if (_requestCompleted) return;
base.RaisePostBackEvent(sourceControl, eventArgument);
}
protected internal override void Render(HtmlTextWriter writer)
{
if (_requestCompleted) return;
base.Render(writer);
}
protected internal override void OnPreRender(EventArgs e)
{
if (_requestCompleted) return;
base.OnPreRender(e);
}
... and so on
}
Puede que no me quede bien respondiendo la pregunta directamente, pero me gustó ver tu actualización sobre la experiencia del usuario. Prefiero tu enfoque sugerido.
Me gusta dar un error 410 para los identificadores que no son válidos y extenderlo un poco (traducido de C #):
Protected Sub ItemDoesNotExist()
''item does not exist, serve up error page
ControlsPanel.Visible = False
ErrorPanel.Visible = True
''add meta tags for noindex
Dim mymeta As New HtmlMeta()
mymeta.Name = "robots"
mymeta.Content = "noindex"
Page.Header.Controls.Add(mymeta)
''RESPOND WITH A 410
Response.StatusCode = 410
Response.Status = "410 Gone"
Response.StatusDescription = "Gone"
Response.TrySkipIisCustomErrors = True
''important for IIS7, otherwise the Custom error page for 404 shows.
Page.Title = "item gone"
End Sub