Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Thomas Lebrun

Tout sur WPF, LINQ, C# et .NET en général !

Actualités

[Silverlight 3] Silverlight 3 et la gestion des erreurs WCF

Tous les développeurs Silverlight ont déjà rencontré l’erreur suivante lorsqu’ils travaillent avec WCF:

The remote server returned an error: NotFound.

Quoi de plus frustrant de savoir qu’une exception est déclenchée, sur le serveur, mais de ne pas en connaitre la cause !

Avec Silverlight 2, pour débugger ce genre d’erreurs, il n’y a pas 36 solutions: on remonte ces manches et on se triture les méninges pour essayer de savoir quelle exception pourrait survenir, sur le code serveur.

Avec Silverlight 3, le modèle de programmation des erreurs SOAP a été amélioré et on est maintenant capable de remonter, coté client, l’erreur qui est survenu sur le serveur. Mais avant de rentrer dans les détails, il convient de chercher pourquoi, à l’heure actuelle, l’erreur SOAP n’est pas renvoyée au client.

En fait, lorqu’une exception est déclenchée, celle-ci provoque l’envoi d’un code réponse HTTP 500. Or, dans son implémentation, Silverlight n’est capable de comprendre/intercepter que les code 200. Pour faire en sorte que le message d’erreur soit correctement envoyé au client, il est donc nécessaire, lorsqu’une exception est déclenchée, de lui faire renvoyer un code d’erreur 200, dans tous les cas.

Pour cela, plusieurs étapes sont nécessaires. La première consiste à créer, coté serveur, un “endpoint behavior” qui va se charger d’intercepter les réponses envoyées au client et, lorsqu’il s’agit de fautes, de renvoyer le code réponse 200:

public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior

{

    #region Properties

 

    public override Type BehaviorType

    {

        get { return typeof(SilverlightFaultBehavior); }

    }

 

    #endregion

 

    #region Override Methods

 

    protected override object CreateBehavior()

    {

        return new SilverlightFaultBehavior();

    }

 

    #endregion

 

    #region IEndpointBehavior Members

 

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

    {

        var inspector = new SilverlightFaultMessageInspector();

            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);

    }

 

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

    {

    }

 

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

    {

    }

 

    public void Validate(ServiceEndpoint endpoint)

    {

    }

 

    #endregion

 

    #region Nested type: SilverlightFaultMessageInspector

 

    public class SilverlightFaultMessageInspector : IDispatchMessageInspector

    {

        #region IDispatchMessageInspector Members

 

        public void BeforeSendReply(ref Message reply, object correlationState)

        {

            if (reply.IsFault)

            {

                var property = new HttpResponseMessageProperty();

 

                // Here the response code is changed to 200.

                property.StatusCode = HttpStatusCode.OK;

 

                reply.Properties[HttpResponseMessageProperty.Name] = property;

            }

        }

 

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)

        {

            // Do nothing to the incoming message.

            return null;

        }

 

        #endregion

    }

 

    #endregion

}

Ensuite, il faut enregister ce behavior dans le fichier web.config, puis indiquer à notre service qu’il doit l’utiliser:

<system.serviceModel>

    <extensions>

      <behaviorExtensions>

        <add name="silverlightFaults"

             type="DemoErreursWCF.Web.SilverlightFaultBehavior, DemoErreursWCF.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

      </behaviorExtensions>

    </extensions>

 

    <behaviors>

 

      <endpointBehaviors>

        <behavior name="SilverlightFaultBehavior">

          <silverlightFaults />

        </behavior>

      </endpointBehaviors>

 

      <serviceBehaviors>

        <behavior name="DemoErreursWCF.Web.ServiceWithGoodErrorBehavior">

          <serviceMetadata httpGetEnabled="true"/>

          <serviceDebug includeExceptionDetailInFaults="true"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

    <bindings>

      <customBinding>

        <binding name="customBinding1">

          <binaryMessageEncoding/>

          <httpTransport/>

        </binding>

      </customBinding>

    </bindings>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

    <services>

      <service behaviorConfiguration="DemoErreursWCF.Web.ServiceWithGoodErrorBehavior" name="DemoErreursWCF.Web.ServiceWithGoodError">

        <endpoint address=""

                  binding="customBinding"

                  bindingConfiguration="customBinding1"

                  contract="DemoErreursWCF.Web.ServiceWithGoodError"

                  behaviorConfiguration="SilverlightFaultBehavior"/>

        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

      </service>

    </services>

  </system.serviceModel>

A présent, il ne reste plus qu’à écrire une méthode dans notre service WCF et lui faire déclencher une exception:

public class ServiceWithGoodError

{

    [OperationContract]

    [FaultContract(typeof(ArithmeticFault))]

    public double Divide(int a, int b)

    {

        try

        {

            return Convert.ToDouble(a / b);

        }

        catch(DivideByZeroException)

        {

            var fault = new ArithmeticFault

                       {

                           Description = "Cannot divide by zero !"

                       };

 

            throw new FaultException<ArithmeticFault>(fault, new FaultReason(fault.Description));

        }

    }

}

 

public class ArithmeticFault

{

    public string Description { get; set; }

}

Notez au passage l’utilisation de l’attribut FaultContract, qui permet d’indiquer que la méthode va, potentiellement, retournée une exception du type indiqué.

A présent, il ne reste plus, coté client, qu’à utiliser notre service et sa méthode:

private void btnCallGoodError_OnClick(object sender, RoutedEventArgs e)

{

    var client = new ServiceWithGoodErrorClient();

    client.DivideCompleted += this.ClientGoodErrorDivideCompleted;

    client.DivideAsync(5, 0);

}

 

private void ClientGoodErrorDivideCompleted(object sender, DivideCompletedEventArgs e)

{

    if (e.Error == null)

    {

        this.tbResults.Text = e.Result.ToString();

    }

    else if (e.Error is FaultException<ArithmeticFault>)

    {

        var fault = e.Error as FaultException<ArithmeticFault>;

 

    this.tbResults.Text = Environment.NewLine + fault.Detail.Description;

    }

}

Et voici ce que l’on obtient en cas d’erreur:

image

Inversement, voici ce que l’on obtient dans le cas où l’on utilise pas cette technique:

image

Comme vous pouvez le constater, aucunes informations n’est passée/indiquée quant à la nateure de l’exception déclenchée coté serveur !

 

Voila qui s’avère tout de même bien pratique! Smile

 

Un lien dans lequel vous pourrez retrouver tout cela, est plus encore: http://msdn.microsoft.com/en-us/library/dd470096(VS.95).aspx

 

 

A+

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: mardi 4 août 2009 10:03 par Thomas LEBRUN
Classé sous : , ,

Commentaires

minsou a dit :

Je te conseille également cet excellent article sur la manière de gérer les exceptions WCF de façon plus générale

http://msdn.microsoft.com/fr-fr/windows/bb872353.aspx

MinSou

# août 4, 2009 18:19

h2s a dit :

je ne comprends pas pourquoi cela ne marche pas en mode DEBUG (F5) par contre cela fonctionne très bien en mode non DEBUG (Crtl + F5).

# août 9, 2009 15:55

Antony a dit :

Bonjour à tous !

Merci beaucoup pour cet article qui m'a été très utile !

J'aimerai améliorer la chose en créant un objet "ExceptionHandler" qui hérite de "FaultException".

Seulement, côté client mon objet ExceptionHandler est "perdu" il me retourne juste un objet FaultException.

Comment puis-je procéder ? Merci d'avance !

# février 25, 2010 12:26
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- [SharePoint] Les sessions TechDays 2012… par Le blog de Patrick [MVP SharePoint] le il y a 6 heures et 22 minutes

- TechDays Paris 2012 : Session pleinière jour 3 par Blog Technique de Romelard Fabrice le 02-09-2012, 11:01

- Mishra Reader : un lecteur RSS très Zune Style en Open Source ! par Cyril Sansus le 02-09-2012, 08:28

- [framework 4] Les Tasks et le Thread UI par Fathi Bellahcene le 02-09-2012, 00:33

- Workflow Foundation 3 a un pied dans la tombe par Blog de Jérémy Jeanson le 02-08-2012, 22:15

- TechDays Paris 2012 : Nouvelles tendances du poste de travail - Bring Your own PC par Blog Technique de Romelard Fabrice le 02-08-2012, 19:42

- TechDays Paris 2012 : System Center Service Manager 2012 Vue d’ensemble par Blog Technique de Romelard Fabrice le 02-08-2012, 17:32

- TechDays Paris 2012 : Pleinière second jour par Blog Technique de Romelard Fabrice le 02-08-2012, 16:23

- TechDays Paris 2012 : Retour d'expérience sur la mise en place d'un Cloud Privé par Blog Technique de Romelard Fabrice le 02-08-2012, 16:04

- TechDays Paris 2012 : Comment SharePoint a sauvé mes TechDays par Blog Technique de Romelard Fabrice le 02-07-2012, 23:59