Call WCF web services using jQuery

Although the implementation is simple and straightforward I decided to make a post about this because I had to solve a few unpleasant issues in the long way from development to deployment. So this is going to be a long story.

1. Create the service

namespace App_Code.Services
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class WCFService
    {
        [OperationContract]
        public string SendMessageToServer(string clientMessage, bool throwError)
        {           
            return string.Format("The following message was received on the server: '{0}'.", clientMessage);
        }
    }
}

2. Set the web.config Please note that the service name must be fully qualified (<service name=”App_Code.Services.WCFService”>). Otherwise you’ll get a 400 – Bad Request error.

<system.serviceModel>
  <bindings>
    <webHttpBinding>
      <binding name="longRequests" maxReceivedMessageSize="2147483647">
        <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
      </binding>
    </webHttpBinding>
  </bindings> 
  <behaviors>
    <endpointBehaviors>
      <behavior name="WCFServiceBehavior">
        <enableWebScript/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
  <services>
    <service name="App_Code.Services.WCFService">
      <endpoint address="" behaviorConfiguration="WCFServiceBehavior" binding="webHttpBinding" bindingConfiguration="longRequests" contract="App_Code.Services.WCFService"/>
    </service>
  </services>
</system.serviceModel>  

3. Call the service using jquery

function MakeAjaxCall(throwError) {
    var data = {};
    data.clientMessage = $('#tbMessage').val();
    data.throwError = throwError;

    $.ajax({
        url: 'WCFService.svc/SendMessageToServer',
        data: JSON.stringify(data),
        type: "POST",
        processData: false,
        contentType: "application/json; charset=utf-8",
        timeout: 300000,
        dataType: "json",

        success: function (data, textStatus, jqXHR) {
            alert(data.d);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            alert("Error status code: " + jqXHR.status + " - " + jqXHR.statusText);
        }
    });
};

4. Error logging This works fine but I also wanted to log any server error that might appear in the service. So I had to create a class that implements the IErrorHandler interface and a class that extends the IEndpointBehavior interface and calls the custom error handler. To bind them with my service I used a custom BehaviorExtensionElement.

public class AjaxErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return false;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        //Log the error
    }
}
public sealed class AjaxErrorHandlerServiceBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        if (endpoint == null || endpointDispatcher == null ||  endpointDispatcher.ChannelDispatcher == null)
            return;

        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new AjaxErrorHandler());
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}
public class AjaxErrorHandlerExtensionElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new AjaxErrorHandlerServiceBehavior();
    }

    public override Type BehaviorType
    {
        get
        {
            return typeof(AjaxErrorHandlerServiceBehavior);
        }
    }
}

Finally, the new AjaxErrorHandlerExtensionElement will be added in the web.config as an extension to the service.

<system.web>
  <compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.serviceModel>
  <extensions>
    <behaviorExtensions>
      <add name="errorHandler" type="App_Code.ErrorHandler.AjaxErrorHandlerExtensionElement, App_Code"/>
    </behaviorExtensions>
  </extensions>
  <behaviors>
    <endpointBehaviors>
      <behavior name="WCFServiceBehavior">
        <enableWebScript/>
        <errorHandler />
      </behavior>
    </endpointBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
  <services>
    <service name="App_Code.Services.WCFService">
      <endpoint address="" behaviorConfiguration="WCFServiceBehavior" binding="webHttpBinding" contract="App_Code.Services.WCFService"/>
    </service>
  </services>
</system.serviceModel>
</system.serviceModel>

Now you’ll probably get a warning from Visual Studio saying that “The element ‘behavior’ has invalid child element ‘errorHandler’.”. This happens because the IntelliSense tries to find an element with the name ‘errorHandler’ in the xml schema definition for the behavior extension element. So just ignore the warning, this will never generate a run-time exception.

Before finding this solution I’ve lost some time because, at the beginning I tried to derive the AjaxErrorHandlerServiceBehavior from the .net framework WebHttpBehavior class. This worked but somehow changed the response format to plain text instead of JSON with a ‘d’ Parameter. The ‘d’ parameter is created by the WebScriptEnablingBehavior class that was bound to the service using the <enableWebScript> attribute in web.config.

Finally everything works fine… so if you want to see the full code you can download it from CodePlex.

Leave a comment