2009-01-24 7 views
13

J'utilise la balise [System.Web.Script.Services.ScriptService] pour utiliser les services Web appelables à partir du javascript côté client. Ce dont j'ai besoin, c'est d'un moyen de consigner globalement toutes les exceptions non gérées dans ces méthodes. Du côté client, j'obtiens le callback d'erreur et je peux procéder à partir de là, mais j'ai besoin d'un catch côté serveur pour enregistrer l'exception.Annotez globalement des exceptions à partir des services ASP.NET [ScriptService]

Le gars à l'adresse suivante: http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx

suggère que cela ne peut être fait.

Est-ce exact? Est-ce que je dois sérieusement aller à chaque webmethod dans le système entier et essayer/attraper la méthode dans son ensemble.

Répondre

14

Vous pouvez utiliser un module HTTP pour capturer le message d'exception, la trace de la pile et le type d'exception généré par la méthode de service Web.

Tout d'abord quelques arrière-plan ...

  • Si une méthode de service Web génère une exception la réponse HTTP a un code d'état de 500.

  • Si les erreurs personnalisées sont désactivées alors le service web renvoie le message d'exception et trace la pile au client en tant que JSON.Par exemple:
    {"Message":"Exception message","StackTrace":" at WebApplication.HelloService.HelloWorld() in C:\Projects\Stackoverflow Examples\WebApplication\WebApplication\HelloService.asmx.cs:line 22","ExceptionType":"System.ApplicationException"}

  • Lorsque des erreurs personnalisées sont allumées, le service Web renvoie un message par défaut au client et supprime la pile trace et le type exception:
    {"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}

Alors ce que nous besoin de faire est de mettre des erreurs personnalisées hors service pour le service Web et branchez un module HTTP qui:

  1. C Hecks si la demande est une méthode de service Web
  2. Vérifie si une exception a été levée - à savoir, un code d'état de 500 est retourné
  3. Si 1) et 2) sont vraies alors obtenir le JSON original qui serait envoyé au client et le remplacer par la valeur par défaut JSON

le code suivant est un exemple d'un module HTTP qui fait cela:

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() { } 
}

ce module utilise le filtre suivant pour remplacer le contenu envoyé à le client et de stocker les octets d'origine (qui n le message d'exception, la trace de pile et le type d'exception):

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; } 
    } 
}

Cette méthode nécessite la désactivation des erreurs personnalisées pour les services Web. Vous souhaiterez probablement conserver des erreurs personnalisées pour le reste de l'application afin que les services Web soient placés dans un sous-répertoire. Les erreurs personnalisées peuvent être désactivées dans ce répertoire uniquement à l'aide d'un fichier web.config qui remplace le paramètre parent.

+0

Bien que je ne m'occupe pas de cela sur mon projet actuel, c'est probablement le seul moyen de détecter de manière fiable toutes ces erreurs. – Clyde

+0

J'ai essayé celui-ci, mais en quelque sorte mon contexte.Response.Filter.length a lancé une exception de type 'System.NotSupportedException'. Ça ne marche tout simplement pas. Je pense que c'est très proche, ma sortie est exactement comme tu l'as dit. et j'ai reçu 500 codes d'état. – maxisam

-1

Dans ASP.Net, il est possible d'intercepter toutes les exceptions gérées en utilisant un global error handler, bien que l'article de blog suggère que cela ne fonctionnerait pas mais que vous pourriez essayer cette approche en essayant de relancer l'erreur. Une autre idée serait de regarder l'open source elmah (Error Logging Modules and Handlers) pour ASP.Net qui pourrait aider ou quelqu'un dans cette communauté peut avoir une idée.

+1

Oui, je ne suis pas sûr que vous ayez vraiment compris le problème. L'exception * est * interceptée par le code de service de script ASP.NET - interceptée avant que je puisse l'attraper. Le gestionnaire d'erreurs global ne fonctionne donc pas. Le cadre envoie une erreur côté client afin que je puisse afficher un message, mais il n'y a pas de crochet de serveur – Clyde

+0

Cependant, le lien d'elmah est intéressant ... Je serai sûr de vérifier cela – Clyde

+0

Je voudrais deuxième ELMAH. J'utilise cela exactement pour ce but moi-même. –

1

Je sais que cela ne répond pas à la question per-say, mais je suis allé sur ma propre quête il y a un certain temps pour le savoir et irait les mains vides. A terminé l'emballage de chaque appel de service Web dans un try/catch, et le catch appelle notre consignateur d'erreurs. Suce, mais ça marche.

3

Vous exécutez la procédure stockée dans le backend. Ensuite, pour une seule variable, elle renvoie plus de 1 valeur. À cause de cela, un conflit se produit, et, cette erreur est lancée.