Design, Build and Release

Ramblings of IainJMitchell on C#, Javascript and Software Craftsmanship

Getting JQuery and WCF to talk (part two of two)

15 comments

Introduction

In my last article I covered how to get JQuery to call a WCF method. This included the passing of arguments and the handling of any return values. In this, the second part, I will cover the final piece of the JQuery / WCF communication jigsaw, the throwing and handling of faults.

Setting up the ErrorHandler

When any type of FaultException is raised from a web enabled WCF service the default behavior is to throw a HTTP 400 error. If the includeExceptionDetailInFaults service behavior is enabled then a serialized stack trace is also sent across the wire (I strongly advise turning this off before your code goes to deployment).

However, we often need to send more detailed fault information to the JQuery client, as the client may respond differently to different types of error.

To achieve this we need to intercept the outgoing error message and insert a JSON serialised version of the error message. Fortunately, WCF provides extendible error handling behavior that will allow us to alter the error message to our needs.

The first part of adding this behavior is to define a custom error handler to convert any exceptions raised into a Json formatted fault. Here is a custom error handler I use for this conversion:

public class JsonErrorHandler: IErrorHandler
{
  #region Public Method(s)
  #region IErrorHandler Members
  ///
  /// Is the error always handled in this class?
  ///
  public bool HandleError(Exception error)
  {
    return true;
  }

  ///
  /// Provide the Json fault message
  ///
  public void ProvideFault(Exception error, MessageVersion version,
    ref Message fault)
  {
    fault = this.GetJsonFaultMessage(version, error);

    this.ApplyJsonSettings(ref fault);
    this.ApplyHttpResponseSettings(ref fault,
      System.Net.HttpStatusCode.BadRequest, Resources.I_StatusMessage);
  }
  #endregion
  #endregion 

  #region Protected Method(s)
  ///
  /// Apply Json settings to the message
  ///
  protected virtual void ApplyJsonSettings(ref Message fault)
  {
    // Use JSON encoding
    var jsonFormatting =
      new WebBodyFormatMessageProperty(WebContentFormat.Json);
    fault.Properties.Add(WebBodyFormatMessageProperty.Name, jsonFormatting);
  }

  ///
  /// Get the HttpResponseMessageProperty
  ///
  protected virtual void ApplyHttpResponseSettings(
    ref Message fault, System.Net.HttpStatusCode statusCode,
    string statusDescription)
  {
    var httpResponse = new HttpResponseMessageProperty()
    {
      StatusCode = statusCode,
      StatusDescription = statusDescription
    };
    fault.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
  }

  ///
  /// Get the json fault message from the provided error
  ///
  protected virtual Message GetJsonFaultMessage(
    MessageVersion version, Exception error)
  {
    BaseFault detail = null;
    var knownTypes = new List<Type>();
    string faultType = error.GetType().Name; //default

    if ((error is FaultException) &&
        (error.GetType().GetProperty("Detail") != null))
    {
      detail =
        (error.GetType().GetProperty("Detail").GetGetMethod().Invoke(
         error, null) as BaseFault);
      knownTypes.Add(detail.GetType());
      faultType = detail.GetType().Name;
    }

    JsonFault jsonFault = new JsonFault
    {
      Message = error.Message,
      Detail = detail,
      FaultType = faultType
    };

    var faultMessage = Message.CreateMessage(version, "", jsonFault,
      new DataContractJsonSerializer(jsonFault.GetType(), knownTypes));

    return faultMessage;
  }
  #endregion
}

The IErrorHandler interface defines two methods for dealing with a fault. The first HandleError() gives a response to say whether this error handler has dealt with the exception. In the case of my error handler, it handles ALL outbound exceptions/faults so it always returns true.

The second method is ProvideFault(), this takes in a ref to the outgoing message and passes in the exception that has been raised. As you’ve probably guessed this is where the actual fault is placed into the message. In my solution this calls a number of protected methods to build up the message and it’s settings.

I have defined a protected method called GetJsonFaultMessage() to build the new outgoing message. In my solution it is testing whether the exception is a FaultException with detail and if so it appends the Fault DataContract contained in the detail to the DataContract I am placing in the message (JsonFault – It’s declaration is omitted).

Once you have a fault message to send out we need to ensure that the message is going to be serialised into Json and treated as a Http response. This is what the other two protected methods ApplyJsonSettings() and ApplyHttpResponseSettings() are performing.

So, now we have our error handler we need to be able to attach this onto our Json WCF service endpoint. In order to do this we’ll need to write a custom behavior and behavior element.

Setting up the Custom Behavior

To be able to apply the error handler we need to create a custom WebHttpBehavior to insert it into the channel dispatcher associated with our endpoint. This also has to remove any default behaviors that have been added to the channel dispatcher.

Below is an example of how to construct this custom WebHttpBehavior:

public class JsonErrorWebHttpBehavior: WebHttpBehavior
{
  #region Protected Method(s)
  /// 
  /// Add the json error handler to channel error handlers
  /// 
  protected override void AddServerErrorHandlers(ServiceEndpoint endpoint,
    System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
  {
    // clear default error handlers.
    endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();

    // add the Json error handler.
    endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(
      new JsonErrorHandler());
  }
  #endregion
}

To be able to apply this behavior to an endpoint through the configuration file we also require a BehaviorElement defined – like the one defined below:

public class JsonErrorWebHttpBehaviorElement : BehaviorExtensionElement
{
  /// 
  /// Get the type of behavior to attach to the endpoint
  /// 
  public override Type BehaviorType
  {
    get
    {
      return typeof(JsonErrorWebHttpBehavior);
    }
  }

  /// 
  /// Create the custom behavior
  /// 
  protected override object CreateBehavior()
  {
    return new JsonErrorWebHttpBehavior();
  }
}

Configuring the Json Custom Error Behavior

Now that we have our custom error handler and custom behavior, we just need to alter our services web.config file to define the custom behavior and associate it with our Json enabled WCF service.

Here is an updated web.config from the previous article, which has the error behavior added to the service endpoint:
Web.config set up with Json error handling behavior

The behavior extensions section includes an entry for our previously defined JsonWebHttpBehaviorElement. N.B. The type must be the full name of the class which includes namespace, assembly, version, culture and public key token. If this is not all specified then WCF will probably fail in the loading of the behavior.

Our newly defined behavior extension (jsonWebHttp) is included in the JsonBehavior endpoint behavior configuration. This has already been defined as the behaviorConfiguration for out service “MyApp.MyService”, so our WCF service will now return Json faults.

The JQuery client side

Here is the Jquery.Ajax call from the previous article, altered to include the WCF fault handling:

 $.ajax({
   type: "POST",
   contentType: "application/json; charset=utf-8",
   url: "Services/MyApp.svc/HelloWorld",
   data: '{"name":"Iain"}',
   dataType: "json",
   success: function(response) {
     alert(response.HelloWorldResult);
   },
   error: function(message) {
     var jsonFault = JSON.parse(message.responseText);
     alert(jsonFault.Message);
   }
});

As you can see, the error handling function has been altered to create a jsonFault object from the JSON contained in the responseText property of the message. I’m using the JSON.parse(string) function of this JSON library to perform the conversion. Once we have the object we can access the WCF fault data contract properties on it (this is my outer JsonFault fault class, the .Detail property would contain the specific WCF fault).

Conclusion

In this article I have demostrated how to set up a WCF service to return Json formatted faults and how to catch these in the JQuery Ajax call.

The WCF service requires a custom error handler to convert the fault message contents to JSON and change it to a HTTP response. A custom WebHttpBehavior is required to insert the error handler onto the channel dispatcher of the endpoint, and a behavior element is needed to be able to add this as a behavior extension in the web.config. Once all of these are in place the service can be configured in the web.config to use the JSON error handler.

At the JQuery client we just need to define an error handler on the Ajax call. To access the returned fault we just have to simply JSON.parse the return messages responseText property.

Written by IainJMitchell

January 21st, 2010 at 8:26 am

Posted in .NET,C#,JQuery,WCF

Tagged with , , ,

15 Responses to 'Getting JQuery and WCF to talk (part two of two)'

Subscribe to comments with RSS or TrackBack to 'Getting JQuery and WCF to talk (part two of two)'.

  1. I’m trying to implement your solution, but you reference too classes BaseFault and JsonFault without providing the source. Can you provide the details of these classes?

    Thanks in advance.

    Jeffrey Hines

    29 Mar 10 at 10:59 pm

  2. Apologies!
    The fault classes are quite simple.

    [DataContract]
    public abstract class BaseFault
    {
    #region Properties
    ///
    /// The fault message
    ///
    [DataMember]
    public string Message
    {
    get;
    set;
    }
    #endregion
    }

    [DataContract]
    public class JsonFault: BaseFault
    {
    #region Properties
    ///
    /// The detail of the fault
    ///
    [DataMember]
    public BaseFault Detail
    {
    get;
    set;
    }

    ///
    /// The type of the fault
    ///
    [DataMember]
    public string FaultType
    {
    get;
    set;
    }
    #endregion

    All my faults in WCF will inherit from BaseFault, so they can all be attached to the JsonFault in the detail property.

    Hope that helps

    Iain

    admin

    13 Apr 10 at 1:20 pm

  3. Trying to use the same technique using WebInvoke.
    Works good when method accept complex data contract object as parameter.
    But for simple method like
    [WebInvoke]
    GetQuote(string contentId)
    always receive serialization error.. Seem to me it tries to parse input as xml…
    {“There was an error deserializing the object of type System.String. End element ‘root’ from namespace ” expected. Found element ‘contentId’ from namespace ”.”}
    Have you encountered the same? Have you find solution for it?

    Yauhen

    14 Apr 10 at 6:27 pm

  4. Are you setting the ResponseFormat and RequestFormat to Json on the [WebInvoke] attribute?

    [WebInvoke(Method = "POST",
    BodyStyle = WebMessageBodyStyle.Wrapped,
    ResponseFormat = WebMessageFormat.Json,
    RequestFormat= WebMessageFormat.Json) ]

    admin

    15 Apr 10 at 8:42 am

  5. Hi,

    I was trying to use your code. But, I was not specifying the version,culture and public key in web.config file. It was throwing me this general exception(HTTP Error 500.0 – System.ServiceModel.ServiceActivationException
    The page cannot be displayed because an internal server error has occurred.). When I was debugging it using fiddler. Then I tried debugging and I was able to hit the line ” return typeof(JsonErrorWebHttpBehavior);” inside “JsonErrorWebHttpBehaviorElement”. That means configuration is being loaded. But after that It was throwing an exception any clue what mite be going wrong. It will be great help.. Please respond quickly if you can. I am sure it would not take a long time.

    Regards
    Mohit Thakral

    Mohit

    7 Jul 10 at 4:05 pm

  6. I’m afraid you do need to specify the version, culture and public key of the assembly you are loading. Culture can be neutral and version is likely to be 1.0.0.0, unless you”ver changed the version number in your assembly. To get a key you’ll need to sign the assembly and inspect the dll using ildasm to get the public key.

    Hope that helps.

    Iain

    admin

    8 Jul 10 at 8:45 am

  7. How did you discover this?
    I’m looking for a deeper understanding of this kind of thing. What books/courses/videos do you know of that specifically cover jquery and WCF, maybe also Silverlight 4 + WCF. And maybe WCF extensions.
    Thanks

    IanT8

    26 Apr 11 at 2:01 pm

  8. There isn’t much out there covering jQuery AJAX to WCF integration – one of the reasons why I wrote this post. Really though, the jQuery AJAX is just a POST of JSON data, the really good stuff happens at the WCF side. I’d strongly recommend the Juval Lowy book Programming WCF Services. It gives an excellent overview of how WCF works, which helps when you are writing extensions. The Microsoft MCTS book on WCF is not bad either.

    I’ve also written another post on Tips on WCF JSON serialisation that you might find useful.

    IainJMitchell

    27 Apr 11 at 6:07 pm

  9. To Mohit:
    I had a like problem. I changed method CreateBehavior in class JsonErrorWebHttpBehaviorElement:

    protected override object CreateBehavior()
    {
    JsonErrorWebHttpBehavior behavior = new JsonErrorWebHttpBehavior();
    behavior.DefaultBodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Wrapped;
    return behavior;
    }

    Ivan

    6 Jul 11 at 2:51 pm

  10. Hi Iain,

    Just wanted to say thanks, really helped me out.

    btw why do you have webHttp and JsonWebHttp behaviors in your config?

    Ant

    Anthony Johnston

    27 Jul 11 at 1:56 pm

  11. Two questions:

    1. What is Resources.I_StatusMessage? Visual Studio suggests that it is System.Resources, however I_StatusMessage does not exist in that namespace.

    2. I am unable to get jQuery to actually catch the error.

    My web service throws an error:
    throw new FaultException(“A system error occurred while processing your request.”);

    The error is returned to the client as:
    ?({“Message”:”A system error occurred while processing your request.”,”Detail”:null,”FaultType”:”FaultException”},500);

    However, my jQuery sees the response as a success:
    $.ajax({
    type: “GET”,
    url: url,
    dataType: “json”,
    success: function(response, status, xhr) {
    alert(“Success: ” + response.Message + “, ” + response.Detail + “, ” + response.FaultType + “, ” + status + “, ” + xhr.status);
    },
    error: function(XMLHttpRequest, txtStatus, errorThrown) {
    alert(“Fail: ” + txtStatus + “, ” + errorThrown);
    },
    });

    The jQuery output:
    “A system error occurred while processing your request., null, FaultException, success, 200″

    Any suggestions?

    Lok

    21 Oct 11 at 9:16 pm

  12. 1) The status message is just a string from the resource file, so this can be substituted by any string.
    2) It looks like the wrong html status code is being returned (200), in my example it sets it to ‘System.Net.HttpStatusCode.BadRequest’ which should result in a 400 error – that would result in a call to the error function in the Ajax call.

    IainJMitchell

    25 Oct 11 at 1:40 pm

  13. I first tried using the code exactly as you wrote it. In that case, the error returned to the client was:
    ?({“Message”:”A system error occurred while processing your request.”,”Detail”:null,”FaultType”:”FaultException”},400);

    The client still saw the response as a success message. I then tried changing it to a 500 error to see if it worked any differently.

    Any suggestions as to why it isn’t resulting in a call to the Ajax error function? I don’t know if it is a problem in the Ajax, .Net, or Web.config.

    As far as I can tell, I am using the code and settings that you have provided. (With the exception of the I_StatusMessage parameter.)

    Thank you for the help.

    Lok

    1 Nov 11 at 3:03 pm

  14. I have a problem, when I call a function without argument, all works fin, but when I cal a function with (long) argument, I get an error.
    I tried to add ‘”‘ , ….. nothing.

    var pic = GetPictures(0, OnSucceed, DefaultErrorHandler);

    function GetPictures(albumId,onSuccess, onError) {
    $.ajax({
    type: “POST”,
    url: “Service/WcaService.asmx/GetPictures”,
    data: ‘{‘ + albumId + ‘}’,
    contentType: “application/json; charset=utf-8″,
    dataType: “json”,
    success: function (response) {
    alert(“sucess get albums”);
    onSuccess(response.GetPicturesResult);
    },
    error: function (message) {
    alert(“error has occured (Pictures)”);
    onError();
    }
    });
    }

    thanks

    Joe

    21 Nov 11 at 9:51 pm

  15. What is the signature of the GetPictures() operation contract? I think you may need to include the name of the parameter in the ajax data. For example, if your signature was GetPictures(int albumId), then the data would need to be data: “{id:” + albumId + “}”.

    Iain

    IainJMitchell

    24 Nov 11 at 1:13 pm

Leave a Reply