Passing data from a jquery ajax request to a wcf service fails deserialization?

I use the following code to call a wcf service. If i call a (test) method that takes no parameters, but returns a string it works fine. If i add a parameter to my method i get a wierd error:

{"ExceptionDetail":{"HelpLink":null,"InnerException":null,"Message":"The token '\"' was expected but found '''.","StackTrace":" at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)\u000d\u000a at System.Xml.XmlExceptionHelper.ThrowTokenExpected(XmlDictionaryReader reader, String expected, Char found)\u000d\u000a at System.Runtime.Serialization.Json.XmlJsonReader.ParseStartElement()\u000d\u000a at System.Runtime.Serialization.Json.XmlJsonReader.Read()\u000d\u000a at System.ServiceModel.Dispatcher.DataContractJsonSerializerOperationFormatter.DeserializeBodyCore(XmlDictionaryReader reader, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.DataContractJsonSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)","Type":"System.Xml.XmlException"},"ExceptionType":"System.Xml.XmlException","Message":"The token '\"' was expected but found '''.","StackTrace":" at System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, String res, String arg1, String arg2, String arg3)\u000d\u000a at System.Xml.XmlExceptionHelper.ThrowTokenExpected(XmlDictionaryReader reader, String expected, Char found)\u000d\u000a at System.Runtime.Serialization.Json.XmlJsonReader.ParseStartElement()\u000d\u000a at System.Runtime.Serialization.Json.XmlJsonReader.Read()\u000d\u000a at System.ServiceModel.Dispatcher.DataContractJsonSerializerOperationFormatter.DeserializeBodyCore(XmlDictionaryReader reader, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.DataContractJsonSerializerOperationFormatter.DeserializeBody(XmlDictionaryReader reader, MessageVersion version, String action, MessageDescription messageDescription, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeBodyContents(Message message, Object[] parameters, Boolean isRequest)\u000d\u000a at System.ServiceModel.Dispatcher.OperationFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)\u000d\u000a at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"}

My jquery looks like this, but i tried changing the actual data which i send as a string serialized json (as you can see) to a pure json object with the same sad result.

$.ajax({
    type: "POST",
    contentType: "application/json; charset=utf-8",
    url: "ajax/Statistics.svc/Get7DaysStatistics",
    dataType: "json",
    data: "{'customerId': '2'}",
    timeout: 10000,
    success: function(obj) { updateStatistics(obj.d); },
    error: function(xhr) {
        if (xhr.responseText)          
            $("body").html(xhr.responseText);
        else
            alert('unknown error');
        return;
    }
});

The wcf service looks like this:

    [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic"), OperationContract]
    public string Get7DaysStatistics(string customerId)
    {
        Debug.WriteLine(customerId);
        return "Test done";
    }

It's placed in a a class with the following attributes:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

I won't list the configuration in the web.config to keep this long message "short" but i can post it if anybody thinks they can use it - i just want to stress that i CAN call a method and get a result - string or even a json object i can read from as long as i DON'T pass any data to the wcf service.

Answers


Use double quotes instead of single quotes in the JSON you are sending to the service. That is, change:

data: "{'customerId': '2'}",

to

data: '{"customerId": "2"}',

I've tested this locally and this fixes the problem.

Incidentally, I debugged this using a method I've often used when calling ASMX and WCF services using libraries other than the built-in ASP.NET tools. I called the service using the client proxy created by an asp:ScriptReference and then inspected the request being sent to the server using an HTTP sniffer (such as HttpFox for FireFox) and compared the request to the one being sent by jQuery. Then you can usually quickly see what is different (and so probably wrong) with the request. In this case, it was clear that there was a difference in the POST data being sent.


I have only ever thought that posting is essential for username and password log on functionality so this is the way I encode the JSon parameters I send to the web service...

Here is the Webservice Contract..

    [ServiceContract]
    public interface ILogonService
    {
    [OperationContract]
    [WebInvoke(
      Method = "POST",
      BodyStyle = WebMessageBodyStyle.WrappedRequest,
      ResponseFormat = WebMessageFormat.Json
    )]
    string Logon(string un, string pw);
    }

Here is the JQuery (Note the use of " and ' is important!)

$.ajax({
  type: 'POST',
  url: "/LogonService.svc/Logon",
  data: '{ "un": "' + $('#un').val() + '", "pw": "' + $('#pw').val() + '"}',
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function (data) {    
  }
});

I did a function in jscript that solved the problem for sending data via POST to a WCF service ... follow the code ...

function formatJsonDataToWCFPost(d){

var t = {};
var a = '{';
for (var j = 0; j < d.length; j++) {
    var x = d[j];
    for (var i in x) {
        if (x.hasOwnProperty(i)) {
            var c = j + 1 == d.length ? '}' : ',';
            a += ('"' + i + '":"' + x[i] + '"' + c);
        }
    }
}

return a;

}


I think on your operation you need this attribute:

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

See jQuery AJAX calls to a WCF REST Service for more info.


Need Your Help

HTML5 Local Storage of audio element source - is it possible?

javascript caching html5 audio webkit

I've been experimenting with the audio and local storage features of html5 of late and have run into something that has me stumped.