Search code examples
c#web-serviceswcfxamarin.formsazure-app-service-envrmnt

WCF web service Error in deserializing body of request message for operation


This question comes from this other question. The code is basically the same except for FirstMethod and LastMethod methods which I removed because they was giving problems (I comment it in the same question).

Even with that, I'll copy here the service interface just for referencing. If some of you feel it'll be necessary to copy the complete code here too, I have no problem, I'll copy it here.

Service contract interface

[ServiceContract]
public interface IMJRFFrasCdadesService
{
    [OperationContract]
    string Ping();

    [OperationContract]
    Task<string> GoogleLoginAsync(string email);

    [OperationContract]
    Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string email);
}

Ok, now when I connect to the service it throw this:

            Error: 
Error in deserializing body of request message for operation 'GoogleLogin'. OperationFormatter encountered an invalid Message body. Expected to find node type 'Element' with name 'GoogleLogin' and namespace 'http://tempuri.org/'. Found node type 'Element' with name 'GoogleLoginAsync' and namespace 'http://tempuri.org/' ;

            Trace: 
at (wrapper managed-to-native) System.Object.__icall_wrapper_mono_remoting_wrapper(intptr,intptr)
at (wrapper remoting-invoke) ServiceReference2.IMJRFFrasCdadesService.GoogleLoginAsync(string)
at ServiceReference2.MJRFFrasCdadesServiceClient.GoogleLoginAsync (System.String email) [0x00006] in <e1f3df4dfbf340f09d2768d7fbc33427>:0 
at MJRFFacturasComunidades.Model.WebService.WebServiceClass+<LogEmail>d__6.MoveNext () [0x00023] in <e1f3df4dfbf340f09d2768d7fbc33427>:0  ;

Correct me if I'm wrong, please, but I understand that the client is trying to call a method called GoogleLogin, but the method is called GoogleLoginAsync (as you can see in the code) so that is, it doesn't find the method. Obviously.

But well, VStudio auto generated client code with the WCF Web Service Reference Provider... What am I supposed to do? I don't understand it.

Anyway, I've tried to look at the client generated code (I'll repeat now that I'm a totally newbie with WCF) and see this:

[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "1.0.0.1")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IMJRFFrasCdadesService")]
public interface IMJRFFrasCdadesService
{

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMJRFFrasCdadesService/Ping", ReplyAction="http://tempuri.org/IMJRFFrasCdadesService/PingResponse")]
    System.Threading.Tasks.Task<string> PingAsync();

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMJRFFrasCdadesService/GoogleLogin", ReplyAction="http://tempuri.org/IMJRFFrasCdadesService/GoogleLoginResponse")]
    System.Threading.Tasks.Task<string> GoogleLoginAsync(string email);

    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IMJRFFrasCdadesService/UploadFile", ReplyAction="http://tempuri.org/IMJRFFrasCdadesService/UploadFileResponse")]
    System.Threading.Tasks.Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string email);
}

I suppose the Action parameter of the OperationContractAttribute is what defines what method will be called at the service (again, correct me if I'm wrong, please), so I changed that, just to test it, with this result:

            Error: 
The message with Action 'http://tempuri.org/IMJRFFrasCdadesService/GoogleLoginAsync' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None). ;

            Trace: 
at (wrapper managed-to-native) System.Object.__icall_wrapper_mono_remoting_wrapper(intptr,intptr)
at (wrapper remoting-invoke) ServiceReference1.IMJRFFrasCdadesService.GoogleLoginAsync(string)
at ServiceReference1.MJRFFrasCdadesServiceClient.GoogleLoginAsync (System.String email) [0x00006] in <7e18c3beb694450a8a87883f2950def0>:0 
at MJRFFacturasComunidades.Model.WebService.WebServiceClass+<LogEmail>d__6.MoveNext () [0x00023] in <7e18c3beb694450a8a87883f2950def0>:0  ;

So, no, I was wrong :(

And yeah, that's all I can say, I have no idea why this is happening. Honestly, I thought that the "reference provider" would generate a call to the service method, as it is in the service interface, but well, I suppose I didn't understand it...

Anyway, I don't know what to do now. Any help will be greatly appreciated... again.


Edit:

Ok, I can not get to work the workaruonds for the Xamarin.Forms issue I wrote at the comments, so as suggested by @mahlatse, this is the service contract in the web service right now:

[ServiceContract]
public interface IMJRFFrasCdadesService
{
    [OperationContract]
    string Ping();

    [OperationContract]
    Task<string> GoogleLoginAsync(string email);

    [OperationContract]
    Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string email);
}

And in the Xamarin.Forms client the auto-generated interface, modified by me:

[System.CodeDom.Compiler.GeneratedCodeAttribute("dotnet-svcutil", "1.0.0.1")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName= "ServiceReference1.IMJRFFrasCdadesService")]
public interface IMJRFFrasCdadesService
{
    [System.ServiceModel.OperationContractAttribute(Action=@"http://tempuri.org/IMJRFFrasCdadesService/Ping", ReplyAction=@"http://tempuri.org/IMJRFFrasCdadesService/PingResponse")]
    System.Threading.Tasks.Task<string> PingAsync();

    [System.ServiceModel.OperationContractAttribute(Action=@"http://tempuri.org/IMJRFFrasCdadesService/GoogleLoginAsync", ReplyAction= @"http://tempuri.org/IMJRFFrasCdadesService/GoogleLoginAsyncResponse")]
    System.Threading.Tasks.Task<string> GoogleLoginAsync(string email);

    [System.ServiceModel.OperationContractAttribute(Action=@"http://tempuri.org/IMJRFFrasCdadesService/UploadFileAsync", ReplyAction= @"http://tempuri.org/IMJRFFrasCdadesService/UploadFileAsyncResponse")]
    System.Threading.Tasks.Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string email);
}

As you can see I've modified the ReplyAction parameter too, and I added the @ just to avoid "escaped characters problems". But no change at all.

After reading @tgolisch comment I revised the web.config file and see the binding was https. I revised the client and I was setting the binding as http when I was creating the service client class, so I changed that:

private IMJRFFrasCdadesService _Service;

public WebServiceClass(string url)
{
    _ServiceUrl = url;
    _Service = new MJRFFrasCdadesServiceClient(
        new BasicHttpsBinding(),
        new EndpointAddress(_ServiceUrl));
}

I also changed this method from the auto-generated service client class, because it was creating a http binding when the parameter was referring to https (at the second if it was using System.ServiceModel.BasicHttpBinding instead of the https version):

    private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
    {
        if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_IMJRFFrasCdadesService))
        {
            System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
            result.MaxBufferSize = int.MaxValue;
            result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
            result.MaxReceivedMessageSize = int.MaxValue;
            result.AllowCookies = true;
            return result;
        }
        if ((endpointConfiguration == EndpointConfiguration.BasicHttpsBinding_IMJRFFrasCdadesService))
        {
/*HERE -->*/ System.ServiceModel.BasicHttpsBinding result = new System.ServiceModel.BasicHttpsBinding();
            result.MaxBufferSize = int.MaxValue;
            result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
            result.MaxReceivedMessageSize = int.MaxValue;
            result.AllowCookies = true;
            result.Security.Mode = System.ServiceModel.BasicHttpsSecurityMode.Transport;
            return result;
        }
        throw new System.InvalidOperationException(string.Format("No se pudo encontrar un punto de conexión con el nombre \"{0}\".", endpointConfiguration));
    }

After all this I still get the same result.

Honestly, I'm beginning to ask to myself what's the point in having VStudio to auto generate a code full of errors.

Even with all this I'm not discarding the xamarin issue I pointed at comments yet.


Solution

  • I gave up on this. I've just made a ASP.Net core web api.

    With the WCF web service I spent more than a week between reading, learning and fighting with it: never work as intended.

    With the web api I spent half a day reading and learning (and most of it was this video at youtube's visual studio channel. I've always hated MSDN articles way of explaining things, but this video was a real live saver), and one day and a morning making it work including the Xamarin.Forms client, all with very little previous knowledge about HTTP or ASP.Net.

    Now it all works perfect. Thanks for trying to help :)