Como se describe en Capture all unhandled exceptions automatically with WebService realmente no hay una buena solución.
El motivo por el que no puede capturar HttpApplication.Error etc. tiene que ver con la forma en que RestHandler ha sido implementado por las buenas personas de Microsoft. En concreto, el RestHandler atrapa de forma explícita (asas) la excepción y escribe los detalles de la excepción a la Respuesta:
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)
{
try
{
NamedPermissionSet namedPermissionSet = HttpRuntime.NamedPermissionSet;
if (namedPermissionSet != null)
{
namedPermissionSet.PermitOnly();
}
IDictionary<string, object> rawParams = GetRawParams(methodData, context);
InvokeMethod(context, methodData, rawParams);
}
catch (Exception exception)
{
WriteExceptionJsonString(context, exception);
}
}
Para empeorar las cosas, no hay punto de extensión limpia (que pude encontrar) donde se puede cambiar/extender el comportamiento. Si quiere ir por el camino de escribir su propio IHttpHandler, creo que tendrá que volver a implementar el RestHandler (o RestHandlerWithSession); independientemente Reflector será tu amigo.
Para aquellos que pueden optar por modificar sus webMethods
Si está utilizando Visual Studio 2008 o posterior, utilizando expresiones lambda hace que las cosas no es tan malo (aunque no es solución global/genérico) en términos o extracción de duplicados código.
[WebMethod]
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
public String GetServerTime()
{
return Execute(() => DateTime.Now.ToString());
}
public T Execute<T>(Func<T> action)
{
if (action == null)
throw new ArgumentNullException("action");
try
{
return action.Invoke();
}
catch (Exception ex)
{
throw; // Do meaningful error handling/logging...
}
}
Dónde Ejecutar puede implementarse en una subclase de WebService o como un método de extensión.
ACTUALIZACIÓN: Reflexión mal
Como se ha mencionado en mi respuesta original, se puede abusar de reflexión para obtener lo que desea ... específicamente puede crear su propia HttpHandler que hace uso de la parte interna de la RestHandler a proporcionar un punto de interceptación para capturar detalles de excepción. He incluido un ejemplo de código "inseguro" a continuación para que comiences.
Personalmente, NO usaría este código; pero funciona.
namespace WebHackery
{
public class AjaxServiceHandler : IHttpHandler
{
private readonly Type _restHandlerType;
private readonly MethodInfo _createHandler;
private readonly MethodInfo _getRawParams;
private readonly MethodInfo _invokeMethod;
private readonly MethodInfo _writeExceptionJsonString;
private readonly FieldInfo _webServiceMethodData;
public AjaxServiceHandler()
{
_restHandlerType = typeof(ScriptMethodAttribute).Assembly.GetType("System.Web.Script.Services.RestHandler");
_createHandler = _restHandlerType.GetMethod("CreateHandler", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(HttpContext) }, null);
_getRawParams = _restHandlerType.GetMethod("GetRawParams", BindingFlags.NonPublic | BindingFlags.Static);
_invokeMethod = _restHandlerType.GetMethod("InvokeMethod", BindingFlags.NonPublic | BindingFlags.Static);
_writeExceptionJsonString = _restHandlerType.GetMethod("WriteExceptionJsonString", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(HttpContext), typeof(Exception) }, null);
_webServiceMethodData = _restHandlerType.GetField("_webServiceMethodData", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField);
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
var restHandler = _createHandler.Invoke(null, new Object[] { context });
var methodData = _webServiceMethodData.GetValue(restHandler);
var rawParams = _getRawParams.Invoke(null, new[] { methodData, context });
try
{
_invokeMethod.Invoke(null, new[] { context, methodData, rawParams });
}
catch (Exception ex)
{
while (ex is TargetInvocationException)
ex = ex.InnerException;
// Insert Custom Error Handling HERE...
_writeExceptionJsonString.Invoke(null, new Object[] { context, ex});
}
}
}
}
¿Hay alguna flexibilidad en ¿el requisito de que no puedes modificar los métodos web? –
Bueno, sí, siempre y cuando no tenga que rodearlos a todos en bloques gigantes de prueba/captura ... Si la solución está limpia, entonces estoy abierto a ella. –
Tengo una solución que requiere un bloque Try y una Catch de una sola línea. Es posible que puedas mejorarlo para eliminar el try/catch por completo, no he cavado tan profundo. ¿Quieres publicarlo? –