Escribí un HttpModule para interceptar, evaluar y autorizar solicitudes, verificando si el usuario registrado tiene acceso apropiado a la URL solicitada, en un sistema heredado bastante antiguo escrito en ASP.NET 2.0 (páginas web, no aplicación web), cuyo cliente no desea portar a un marco más nuevo. Las restricciones se han cargado y almacenado en caché en el momento del inicio de sesión.
Todo funciona bien, excepto cuando alguna página contiene un componente <asp:MultiView> o cuando hay un botón que inicia un método ajax. Cuando ocurre una de estas situaciones y el usuario no tiene derechos para acceder a esa URL, aparece un cuadro de alerta con un mensaje de "Error desconocido", que proviene de una ThreadAbortException lanzada por el método Response.End() .
La pregunta es: ¿Por qué mi mensaje "No autorizado" se sobrescribe con "Error desconocido" de la excepción, solo en estas dos situaciones?
¿Hay alguna manera de hacer un sistema de Autorización de Url, usando la base de datos y el almacenamiento en caché y sin saturar Web.config con roles como esos ejemplos antiguos de ASP.NET?
// My module init method. public void Init(HttpApplication context) { context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute); // PreRequestHandlerExecute is the first stage at ASP.NET Pipeline // where we could get a fulfilled Session variable } private void context_PreRequestHandlerExecute(object sender, EventArgs e) { HttpApplication application = (HttpApplication)sender; HttpContext context = application.Context; // additional request filtering/validation/etc. LoggedUser user = (LoggedUser)application.Session["user"]; string path = context.Request.Path; // more checks and rules... if (!checkUserAuthorization(path, user)) { context.Response.Write("<script>alert('Unauthorized. Contact your manager.');</script>"); context.Response.Write("<script>window.history.back();</script>"); context.Response.StatusCode = 403; context.Response.End(); } }EDITAR: Lo que ya probé (sin objetivo):
Response.OutputStream.Close();Response.Flush();HttpApplication.CompleteRequest();es por diseño. debe ignorarlo y agregar una captura para esa excepción.
try { context.Response.End(); } catch{}Prefacio
Después de mucho investigar, finalmente lo conseguí. Teniendo en cuenta ASP.NET 2.0, con respecto a las operaciones de AJAX, el proyecto en el que estoy trabajando usa un componente de Microsoft llamado "Atlas", que a su vez pasó a llamarse ASP.NET AJAX . En el momento en que se escribió este sistema, los desarrolladores usaban la versión beta ASP.NET AJAX (nombre en clave "Atlas") para abordar todas las necesidades de representación parcial y ajax.
Necesitaba profundizar en el código fuente (gracias a Reflector), para comprender e inspeccionar de dónde proviene ese "Error desconocido".
Dentro de Microsoft.Web.Atlas, hay un archivo llamado Microsoft.Web.Resources.ScriptLibrary.*.Atlas.js (donde * podría ser Debug o Release ) que se procesa en tiempo de ejecución a través de un "proxy" WebResource.axd .
Este archivo javascript tiene un error, porque espera que la solicitud de ASP.NET siempre devuelva un código de respuesta HTTP 200 (OK) , que en mi código no está sucediendo (estoy devolviendo un código 403 Forbidden en mi módulo).
Código
De Microsoft.Web.Resources.ScriptLibrary.*.Atlas.js tomado de WebResource.axd :
this._onFormSubmitCompleted = function(sender, eventArgs) { var isErrorMode = true; var errorNode; var delta; if (sender.get_statusCode() == 200) { delta = sender.get_xml(); if (delta) { errorNode = delta.selectSingleNode("/delta/pageError"); if (!errorNode) { isErrorMode = false; } } } if (isErrorMode) { if (errorNode) { pageErrorMessage = errorNode.attributes.getNamedItem('message').nodeValue; } else { pageErrorMessage = 'Unknown error'; } this._enterErrorMode(pageErrorMessage); return; } // Code continues. } A partir de este código, podemos ver que, dado que el código de respuesta no es un 200 OK , la variable errorNode no se establecerá, y esta if (errorNode) siempre será false .
En este caso, me quedaron dos opciones: devolver siempre HTTP 200 y modificar todas las páginas que tienen un <atlas:ScriptManager> y agregar una etiqueta ErrorTemplate en cada una, o reemplazar ese script con uno que considere respuestas que non-HTTP 200 , cargándolo debajo de la etiqueta </form> en la página maestra.
Hay muchos tutoriales sobre cómo manejar correctamente los errores al usar ScriptManager y UpdatePanels (uno oficial aquí ), suscribiéndose al evento AsyncPostBackError ), pero esta versión beta (Atlas) simplemente no tiene este evento.