Registrar globalmente las excepciones de los servicios ASP.NET
asp.net-ajax error-handling (4)
En ASP.Net es posible capturar todas las excepciones manejadas con un manejador de error global, aunque la publicación del blog sugiere que esto no funcionaría, pero ¿podría experimentar con este enfoque tratando de volver a generar el error de alguna manera?
Otra idea sería mirar la elmah de código abierto (Módulos de registro de errores y controladores) para ASP.Net que podría ayudar o alguien en esa comunidad puede tener una idea.
Estoy usando la etiqueta [System.Web.Script.Services.ScriptService] para usar servicios web invocables desde el lado del cliente de JavaScript. Lo que necesito es una forma de registrar globalmente cualquier excepción no controlada en esos métodos. En el lado del cliente, obtengo la devolución de llamada de error y puedo continuar desde allí, pero necesito una captura del lado del servidor para registrar la excepción.
El chico en esta url: http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx
sugiere que esto no se puede hacer.
Es eso exacto? ¿De verdad tengo que ir a todos los métodos web en todo el sistema y probar / capturar el método como un todo?
Sé que esto no responde a la pregunta, por decir, pero fui en mi propia búsqueda hace un tiempo para descubrirlo y saldría con las manos vacías. Terminó envolviendo cada llamada de servicio web en un try / catch, y el catch llama a nuestro registrador de errores. Apesta, pero funciona.
Puede usar un módulo HTTP para capturar el mensaje de excepción, el seguimiento de la pila y el tipo de excepción que arroja el método del servicio web.
Primero algunos antecedentes ...
Si un método de servicio web arroja una excepción, la respuesta HTTP tiene un código de estado de 500.
Si los errores personalizados están desactivados, el servicio web devolverá el mensaje de excepción y el seguimiento de la pila al cliente como JSON. Por ejemplo:
{"Message":"Exception message","StackTrace":" at WebApplication.HelloService.HelloWorld() in C:/Projects/ Examples/WebApplication/WebApplication/HelloService.asmx.cs:line 22","ExceptionType":"System.ApplicationException"}
Cuando los errores personalizados están activados, el servicio web devuelve un mensaje predeterminado al cliente y elimina el rastreo de la pila y el tipo de excepción:
{"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
Entonces, lo que tenemos que hacer es establecer los errores personalizados para el servicio web y conectar un módulo HTTP que:
- Comprueba si la solicitud es para un método de servicio web
- Comprueba si se lanzó una excepción, es decir, se devuelve un código de estado de 500
- Si 1) y 2) son verdaderos, obtenga el JSON original que se le enviará al cliente y reemplácelo con el JSON predeterminado.
El siguiente código es un ejemplo de un módulo HTTP que hace esto:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
public class ErrorHandlerModule : IHttpModule {
public void Init(HttpApplication context) {
context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
context.EndRequest += OnEndRequest;
}
static void OnPostRequestHandlerExecute(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
// TODO: Update with the correct check for your application
if (context.Request.Path.StartsWith("/HelloService.asmx")
&& context.Response.StatusCode == 500) {
context.Response.Filter =
new ErrorHandlerFilter(context.Response.Filter);
context.EndRequest += OnEndRequest;
}
}
static void OnEndRequest(object sender, EventArgs e) {
HttpApplication context = (HttpApplication) sender;
ErrorHandlerFilter errorHandlerFilter =
context.Response.Filter as ErrorHandlerFilter;
if (errorHandlerFilter == null) {
return;
}
string originalContent =
Encoding.UTF8.GetString(
errorHandlerFilter.OriginalBytesWritten.ToArray());
// If customErrors are Off then originalContent will contain JSON with
// the original exception message, stack trace and exception type.
// TODO: log the exception
}
public void Dispose() { }
}
Este módulo utiliza el siguiente filtro para anular el contenido enviado al cliente y almacenar los bytes originales (que contienen el mensaje de excepción, el seguimiento de la pila y el tipo de excepción):
public class ErrorHandlerFilter : Stream {
private readonly Stream _responseFilter;
public List OriginalBytesWritten { get; private set; }
private const string Content =
"{/"Message/":/"There was an error processing the request./"" +
",/"StackTrace/":/"/",/"ExceptionType/":/"/"}";
public ErrorHandlerFilter(Stream responseFilter) {
_responseFilter = responseFilter;
OriginalBytesWritten = new List();
}
public override void Flush() {
byte[] bytes = Encoding.UTF8.GetBytes(Content);
_responseFilter.Write(bytes, 0, bytes.Length);
_responseFilter.Flush();
}
public override long Seek(long offset, SeekOrigin origin) {
return _responseFilter.Seek(offset, origin);
}
public override void SetLength(long value) {
_responseFilter.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count) {
return _responseFilter.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count) {
for (int i = offset; i < offset + count; i++) {
OriginalBytesWritten.Add(buffer[i]);
}
}
public override bool CanRead {
get { return _responseFilter.CanRead; }
}
public override bool CanSeek {
get { return _responseFilter.CanSeek; }
}
public override bool CanWrite {
get { return _responseFilter.CanWrite; }
}
public override long Length {
get { return _responseFilter.Length; }
}
public override long Position {
get { return _responseFilter.Position; }
set { _responseFilter.Position = value; }
}
}
Este método requiere la desactivación de errores personalizados para los servicios web. Probablemente desee mantener los errores personalizados activados para el resto de la aplicación para que los servicios web se ubiquen en un subdirectorio. Los errores personalizados se pueden desactivar en ese directorio solo con un web.config que anula la configuración principal.
Ejecuta el Procedimiento almacenado en el back-end. Luego, para una sola variable, devuelve más de 1 valor. Por eso, se produce un conflicto y este error se produce.