Consuming FaultContract with Restful WCF services


I’ve learned something new this morning while doing a proof-of-concept around WCF REST services.

I did a little WPF demo app both hosting and consuming web services.  I was trying to handle faults properly, using fault contracts.  I had experience with SOAP-based WCF web services, so I went the motions, starting by creating a fault contract:

[DataContract]
public class WrongInteger
{
    [DataMember]
    public int Value { get; set; }
}

A fault contract is just a data contract that you throw with an exception.

Then I declared that my service operation can raise that fault:

[WebInvoke(Method = "PUT")]
[OperationContract]
[FaultContract(typeof(WrongInteger))]
void PushInteger(int a);

Then I implemented the operation, throwing the usual fault exception:

  void IPushService.PushInteger(int a)
  {
    if (a == 2)
    {
        throw new FaultException<WrongInteger>(new WrongInteger { Value = 2 });
    }
  }

This code works fine if you’re using SOAP WCF services.  But with REST WCF services, your HTTP response will just be gibberish with an status of bad request.

Digging on the web I found there’s actually a new class, WebFaultException, derived from FaultException that you must use with REST web services:

void IPushService.PushInteger(int a)
{
    if (a == 2)
    {
        throw new WebFaultException<WrongInteger>(new WrongInteger { Value = 2 }, HttpStatusCode.InternalServerError);
    }
}

This will spit out the serialized fault contract into the http-response.  Now for the client side code

using (WebChannelFactory<IPushService> factory = new WebChannelFactory<IPushService>("myService"))
{
    IPushService service = factory.CreateChannel();

    try
    {
        service.PushInteger(2);
    }
    catch (WebFaultException<WrongInteger> ex)
    {
        Dispatcher.Invoke((Action)delegate
        {
            MessageBox.Show(string.Format("Expected exception:  {0}, {1}", ex.Message, ex.Detail.Value));
        });
    }
    catch (Exception ex)
    {
        Dispatcher.Invoke((Action)delegate
        {
            MessageBox.Show(ex.Message);
        });
    }
}

Well, another surprise, that doesn’t work either.  The thrown exception isn’t a fault exception, it’s a protocol exception.

I haven’t found a way to make this work.  Actually, I’ve read that you should craft HTTP request by hand and look at the http-response in order to get your fault.

Well, that’s a bit of a disappointment!  It seems that the fault scenario wasn’t taught through from the get-go.  For instance, the WebFaultException class is new in .NET 4.0.  So in .NET 3.5, you couldn’t fault:  your exceptions would just be generic exceptions.  Now we can transmit a fault but we can’t consume it.  Maybe in .NET 5.0…

It’s especially frustrating since it would be easy to write a WCF inspector to do that job…

If you find a more elegant way, please let me know!

About these ads

3 thoughts on “Consuming FaultContract with Restful WCF services

  1. Did you find a way to catch this in your client? I have the same issue and haven’t found a reasonable solution in my searches so far. Any input is highly appreciated.

    • Hi Arun,

      It has been a while since I faced this issue. I managed to trap the error on the client side with the information (i.e. fault) from the server side, but I had to go low level as with API call you only receive a generic exception.

      As I mention in the blog you could write a WCF extension to handle that and surface the right exception to the client, but for me this would break the dev experience since you would need to know the type of faults that could be sent on the client-side. This all boils down to the fact that with REST there are no WSDL and no service contract whatsoever, hence making ideal for an ‘obvious’ exchange pattern protocol, but once you want to communicate complicated error patterns, REST isn’t really the right tool.

  2. you can write like this:

    public static T InvokeJson(string server, string methodName, string jsonObject)
    {
    string requestUrl = server + methodName;
    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestUrl);
    request.ContentType = “application/json; charset=utf-8″;
    request.Accept = “application/json, text/javascript, */*”;
    request.Method = “POST”;

    using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
    {
    writer.Write(jsonObject);
    writer.Close();
    }

    WebResponse response = request.GetResponse();
    Stream stream = response.GetResponseStream();
    StringBuilder sb = new StringBuilder();

    using (StreamReader reader = new StreamReader(stream))
    {
    while (!reader.EndOfStream)
    {
    sb.AppendLine(reader.ReadLine());
    }
    }
    T output = JSONManager.ToObject(sb.ToString());
    return output;
    }

    and this code invoke like this:

    DoServiceCall(delegate()
    {
    InitializeResponse output = Helper.InvokeJson(SelectedJsonAddress, “Initialize”, sendRequest);
    });

    and catch exception like this:

    public void DoServiceCall(DoServiceFunction function)
    {
    try
    {
    function();
    }
    catch (WebException ex)
    {
    HandleException(ex);

    Stream stream = ex.Response.GetResponseStream();
    StringBuilder sb = new StringBuilder();
    using (StreamReader reader = new StreamReader(stream))
    {
    while (!reader.EndOfStream)
    {
    sb.AppendLine(reader.ReadLine());
    }
    }
    string msg = sb.ToString();
    writeOutputToControl(msg);
    FacadeException errMsg = JSONManager.ToObject(msg);

    writeOutputToControl(string.Format(“Content: {0}ErrorCode: {1}{2}Message: {3}{4}DebugMessage:{5}”,
    Environment.NewLine, errMsg.ErrorCode,
    Environment.NewLine, errMsg.Message,
    Environment.NewLine, errMsg.DebugMessage));
    }
    catch (Exception e)
    {
    HandleException(e);
    }
    finally
    {
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s