Freigeben über


Exception Handling in WCF Web Service

Exception handling in ASMX web services was a painful job. Information was getting lost over wire / serialization; it was tricky to get fault code back and having a strongly type mapping between service raised exception and client consumed exception was a cumbersome task.

But things are not the same with windows communication foundation (WCF) service. In WCF, you can define a custom fault exception, get it populated and raised at service side as per error/exception and then nicely consume the same at client side. I am going to cover exception handling aspects of WCF service in this blog using an example.

In WCF development, exception handling method is very simple and it uses concept of contract definition. Just like data and service contract, you can also define fault contract and add behavior to it in service behavior. Let’s start with following example,

First define a data contract which you want to use to capture service side fault/exception,

[DataContract]

public enum FaultCode

  {

      [EnumMember]

      ERROR,

      [EnumMember]

      INCORRECT_PARAMETER

  }

[DataContract]

public class SampleFaultException

  {

     [DataMember]

     public FaultCode errorcode;

     [DataMember]

     public string message;

     [DataMember]

     public string details;

  }

I have defined a data contract SampleFaultException which contains three data members “errorcode”, “message” and “details”. “errorcode” is of type FaultCode which is an enum containing two members “ERROR” and “INCORRECT_PARAMEMERTS”.

Now define a service contract. I am using a simple service contract with one method only,

[ServiceContract]

public interface ISampleService

   {

      [OperationContract]

      [FaultContract(typeof(SampleFaultException))]

      int DevideNumber(int x,int y);

   }

You can see that FaultContract of type SampleFaultException is annotated in operation contract details. This means “DevideNumber” will handle appropriate faults/exceptions using SampleFaultException. If you expect more types of faults to occur then you can define more fault contracts and place them in operation contract.

Contract definitions are over. Let’s implement a service over them,

public class SampleService : ISampleService

{

   public int DevideNumber(int x, int y)

   {

         try

        {

             if (y > x)

             {

                 SampleFaultException error = new SampleFaultException();

                 error.errorcode = FaultCode.INCORRECT_PARAMETER;

                 error.message = "value of y can not be greater than x";

                 error.details = "trying to devide with bigger value";

                 throw new FaultException<SampleFaultException>(error);

             }

              return x / y;

          }

         catch (DivideByZeroException dex)

          {

   SampleFaultException error = new SampleFaultException();

              error.errorcode = FaultCode.ERROR;

              error.message = "zero value passed for parameter y";

              error.details = dex.StackTrace;

              throw new FaultException<SampleFaultException>(error);

          }

          catch (Exception ex)

          {

              throw ex;

          }

     }

}

Implemention is quite simple. I am returing x/y in main flow. As part of exception handling logic, in one scenario, I am raising exception in try block as per parameter values. In other, I am casting DivideByZeroException exception into fault contract type SampleFaultException.

Service is ready. Let’s host them in IIS. You can also do selft hosting, windows service based hosting or WPAS (also called WAS) based hosting. I created a virtual directory; and placed a “.svc” file and web.config files.

Add service name in host “.svc” file –

<%@ServiceHost Debug="true" Service="WCFExceptionHandling.SampleService" %>

Add service defintion in web.config file –

<system.serviceModel>

<behaviors>

   <serviceBehaviors>

      <behavior name="Default">

         <serviceDebug includeExceptionDetailInFaults="true"/>

          <serviceMetadata  httpGetEnabled="true"/>

        </behavior>

    </serviceBehaviors>

</behaviors>

<services>

 <service behaviorConfiguration="Default" name="WCFExceptionHandling.SampleService">

     <endpoint address="" binding="basicHttpBinding"

        contract="WCFExceptionHandling.ISampleService" />

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

   </service>

 </services>

</system.serviceModel>

“WCFExceptionHandling” is the namespace of the service I created.

Finally I created a “bin” folder under virtual directory and added dlls containing contracts and services. I accessed URL (some thing like “https://localhost/<virtualditectory>/<Service host file name>.svc”) of hosted service to check if it is showing contract defintions through WSDL.

Things which need attention here are service behaviours. “includeExceptionDetailInFaults” is instructing services to include exception details in faults. “httpGetEnabled” allows services to expose their contract definition through WSDL. You have to enable both of these service behaviours explicitely which are by default flagged off due to security reason. By default WCF service does not reveal its information unless instructed to do so as done above.

It is time to consume this service. I created a windows projects and added service reference for service URL created above. When creating service reference, I added alias name as “WCFService”. Finally I wrote following client code –

  try

  {

WCFService.SampleServiceClient client = new WCFService.SampleServiceClient();

               

int returnValue = client.DevideNumber(4, 2);

  }

catch (FaultException<WCFService.SampleFaultException> faultEx)

  {

       MessageBox.Show(faultEx.Detail.errorcode);

       MessageBox.Show(faultEx.Detail.message);

       MessageBox.Show(faultEx.Detail.details);

  }

catch (Exception ex)

  {

      MessageBox.Show(ex.Message);

 }

You can see that I am creating an instance of service client and then making call to “DevideNumber” method. Interesting part of code is how “SampleFaultException” is handled. “faultEx.Detail” provides every details you captured at service side. I tested this service with 4 and 2 as parameters and it returned 2 (nothing great). When I passed 4,0 as parameters, it raised fault exception with faultEx.Detail.message = "zero value passed for parameter y". And when I passed 4,6, it again raised fault exception with message = “value of y can not be greater than x". Interesting because I am able to capture every thing exactly the same as service raised in exception.

As illustrated through example, You can capture and consume every bit of your exception with simple and extensible approach provided by WCF services. Hope this articles was helpful to understand expection handling methodology in WCF services.

Comments

  • Anonymous
    April 24, 2007
    New version of Anti-XSS library for ASP.NET [Via: gduthie ] Nostalgia - .NET 3.0 Flikr Management Prototype...
  • Anonymous
    April 26, 2007
    Hi,Read about WCF Exception Handling with Enterprise Library 3.0. Might be useful...http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/04/07/Shielding-WCF-Services-with-Exception-Handling-Application-Block-2D00-Part-1.aspxGuy Burstein
  • Anonymous
    May 07, 2007
    Great article, but you probably should change the word "devide" to "divide".
  • Anonymous
    January 03, 2008
    I get an exception"The creator of this fault did not specify a Reason."Help?
  • Anonymous
    January 27, 2008
    Good article, somehow I couldnt find anything useful on offline msdn documentation
  • Anonymous
    April 16, 2008
    The comment has been removed
  • Anonymous
    May 09, 2008
    Short but great!!!! and helpful. Thanks a lotttttttt
  • Anonymous
    May 27, 2008
    I get an exception"The creator of this fault did not specify a Reason."Help?-> Got this one too. Anyone got a solution?
  • Anonymous
    July 15, 2008
    why are you throwing the raw exception in the last catch block??would it be wrapped as the FaultException?