Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF fails to deserialize JSON request

Tags:

json

wcf

I'm trying to write a WCF service to respond to ajax requests, but I'm getting a strange error when it tries to deserialize.

Here's the jQuery:

$.ajax({
    type: 'POST', 
    url: 'http://localhost:4385/Service.svc/MyMethod',
    dataType: 'json',
    contentType: 'application/json',
    data: JSON.stringify({folder:"test", name:"test"})
});

Here's the WCF service definition:

[OperationContract]
[WebInvoke(UriTemplate = "/MyMethod", 
    Method = "*", //Need to accept POST and OPTIONS
    BodyStyle = WebMessageBodyStyle.WrappedRequest, 
    RequestFormat = WebMessageFormat.Json, 
    ResponseFormat = WebMessageFormat.Json)]
string[] MyMethod(string folder, string name);

I get a SerializationException saying: "The OperationFormatter could not deserialize any information from the Message because the Message is empty (IsEmpty = true)."

It occurs in method System.ServiceModel.Dispatcher.PrimitiveOperationFormatter.DeserializeRequest on instruction 00000108 mov dword ptr [ebp-18h],0

I don't see what I'm doing wrong, but it refuses to work for me. Been fighting this all day. Any ideas?

like image 530
Dax Fohl Avatar asked Oct 08 '22 23:10

Dax Fohl


1 Answers

Got it--the answer was staring right at me in the only comments in my code. I needed to accept both POST and OPTIONS (for CORS). The OPTIONS request was coming first, and of course the OPTIONS request comes with no data attached. That was what was causing the parse exception; and the POST never even happened.

The workaround: separate out the POST and OPTIONS into two separate methods, with the same UriTemplate, but with different C# names (WCF requires this).

[OperationContract]
[WebInvoke(UriTemplate = "/MyMethod",
    Method = "POST",
    BodyStyle = WebMessageBodyStyle.WrappedRequest,
    RequestFormat = WebMessageFormat.Json,
    ResponseFormat = WebMessageFormat.Json)]
string[] MyMethod(string folder, string name);

[OperationContract]
[WebInvoke(UriTemplate = "/MyMethod", Method = "OPTIONS")]
void MyMethodAllowCors();

That actually cleans up the code a bit because you don't have to litter all your functions with

if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS") {
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, POST");
    WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, User-Agent");
    return new string[0];
} else if (WebOperationContext.Current.IncomingRequest.Method == "POST") { ... }
like image 194
Dax Fohl Avatar answered Oct 12 '22 16:10

Dax Fohl