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

Error in WCF service hosted with Azure app service: HTTP Error 405.0 - Method Not Allowed


Ok, totally newbie with WCF here, so be patient with me, please.

I have a WCF web service published on Azure app service, and a Xamarin.forms application that must connect to the service.

I made the WCF service, at first I tried to self host it and I made the client on my xamarin app by using the visual studio 2017 "Add service". So it added the Reference.cs file, and I made the proxy class and interface to connect to the service and what not.

I was having problems to connect to the service from the mobile, so I published the service at Azure app service (it's free after all), enabled Azure logs, modified the NLog.config file to log to the trace, modified only the URL in my mobile app, and now the service throws this every time I try to connect to it from my mobile:

HTTP Error 405.0 - Method Not Allowed.The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being used.

I'm reading the error in Visual Studio's Cloud Explorer at LogFiles\DetailedErrors folder.

Honestly, I know almost nothing about HTTP, but I know that I haven't written HTTP in the service. I suppose WCF use it and is using a bad verb at some moment, in some place, but how am I supposed to know where to change it or even how to check it? I just made an interface, a class, and call the methods on that class... no idea where to look for POST or GET.

Anyway, I'm not even sure what code should I post here, but I've read tons of questions about this, so here is some of it:

Service class

public class MJRFFrasCdadesService : IMJRFFrasCdadesService, IDisposable
{
    private Users.UsersClass _Users = new Users.UsersClass();
    private static NLog.Logger _Logger = NLog.LogManager.GetCurrentClassLogger();
    private bool _Disposed;

    public static event EventHandler<ConnectionEventArgs> ConnectionEvent;
    public static event EventHandler<ConnectionEventArgs> DisconnectionEvent;

    public string Ping()
    {
        return "Ok";
    }

    public async Task<string> GoogleLoginAsync(string email)
    {
        NLog.LogManager.Configuration.Variables["email"] = "";
        _Logger.Info("{1}{2}{1}           Comienza google login con email: {0}{1}{2}",
            email,
            Environment.NewLine,
            "-----------------------------------*******************************************");

        var driveHandler = await _Users.NewUserAsync(email);

        try
        {
            var init = await driveHandler.InitDriveAsync(email);
            _Logger.Info("InitDriveAsync finalizado con resultado: {0}", init);
            if (!init || !driveHandler.LoginOK)
            {
                _Logger.Info("Devolviendo mensaje de error: {0}", driveHandler.MsgError);
                return driveHandler.MsgError;
            }
            _Logger.Info("Login ok, devolviendo \"Ok\"");
            return "Ok";
        }
        catch (Exception e)
        {
            _Logger.Error(e);
            return e.ExceptionErrorMessage();
        }
    }

    public async Task<string> UploadFileAsync(byte[] fileBytes, string fileName, string email)
    {
        _Logger.Info("Comienza subida de fichero con nombre {0}", fileName);

        var driveHandler = await _Users.GetUserDriveHandler(email);
        if (driveHandler == null)
        {
            _Logger.Info("No logueado. Devolviendo \"User not logged\"");
            return "User not logged";
        }

        try
        {
            if (!driveHandler.LoginOK)
            {
                _Logger.Info("No logueado. Devolviendo \"Not logged\"");
                return "Not logged";
            }

            var result = await driveHandler.UploadImageAsync(fileBytes, fileName);
            _Logger.Info("Proceso de subida terminado. Resultado: {0}", result);
            if (!result)
            {
                _Logger.Info("Devolviendo mensaje de error: {0}", driveHandler.MsgError);
                return driveHandler.MsgError;
            }
            _Logger.Info("Subida ok, devolviendo \"Ok\"");
            return "OK";
        }
        catch (Exception e)
        {
            _Logger.Error(e);
            return e.ExceptionErrorMessage();
        }
    }

    public void Dispose()
    {
        if (!_Disposed)
        {
            _Users.Dispose();
        }
        _Disposed = true;
    }

    public void FirstMethod()
    {
        if (ConnectionEvent != null)
        {
            string m = "Alguien se ha conectado";
            ConnectionEvent(null, new ConnectionEventArgs()
            {
                Message = m
            });
            _Logger.Info(m);
        }
    }

    public void LastMethod()
    {
        if (DisconnectionEvent != null)
        {
            string m = "Alguien se ha desconectado";
            DisconnectionEvent(null, new ConnectionEventArgs()
            {
                Message = m
            });
            _Logger.Info(m);
        }
    }
}

Interface

[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMJRFFrasCdadesService
{
    [OperationContract]
    string Ping();

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

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

    [OperationContract(IsInitiating = true)]
    void FirstMethod();

    [OperationContract(IsTerminating = true)]
    void LastMethod();
}

Web.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.7.1" />
    <httpRuntime targetFramework="4.7.1"/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Para evitar revelar información de los metadatos, establezca los valores siguientes en false antes de la implementación  -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- Para recibir detalles de las excepciones en los fallos, con el fin de poder realizar la depuración, establezca el valor         siguiente en true. Para no revelar información sobre las excepciones, establézcalo en false antes de la implementación -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>    
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
        Para examinar el directorio raíz de la aplicación web durante la depuración, establezca el valor siguiente en true.
        Establézcalo en false antes de la implementación para evitar revelar información sobre la carpeta de aplicación web.
      -->
    <directoryBrowse enabled="true"/>
  </system.webServer>

</configuration>

That is.

Basically, I've spent more than a week learning and trying to do this, right now I'm super frustrated and I have no idea what's happening, so ANY help would be appreciated, including a simple "hey you know nothing! you must read this <include here link I shoud read>!".


Edit: Ok, due to Mahlatse indications I've tried a pair of things.

Before I was trying to access the service through https://mjrffacturascomunidadeswebservice.azurewebsites.net/ , there appears a message saying Your App Service app is up and running, so I thought everything was ok, but when I tried appending the svc file to that URL, it throwed an exception about asking to a session but using basicHTTPbinding which doesn't support sessions.

I removed both, the session mode parameter from the interface's attribute [ServiceContract(SessionMode = SessionMode.Required)] and the FirstMethod and LastMethod methods, and the exception dissappeared. Now if one browse to https://mjrffacturascomunidadeswebservice.azurewebsites.net/MJRFFrasCdadesService.svc , it says You have created a service. and what not, so I suppose NOW it's truly up and running... so I tried to test it at my app and it throws this:

            Error: 
There was an error on processing web request: Status code 405(MethodNotAllowed): Method Not Allowed ;

            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 <61e2d5b4688b450c9b2865fbef0c9da1>:0 
at MJRFFacturasComunidades.Model.WebService.WebServiceClass+<LogEmail>d__6.MoveNext () [0x00023] in <61e2d5b4688b450c9b2865fbef0c9da1>:0 ;

That's a different error (I suppose at least it's a progress). As you can see the method GoogleLoginAsync is throwing that exception, but now is my app method who is throwing it, not the service: ServiceReference1.MJRFFrasCdadesServiceClient is the class generated by Visual Studio when I added the service reference when I was still self hosting the service.

I think I'm going to remove that reference, add a new one to the service hosted in azure and test again all this.


Edit2:

And yes, it solved that problem, but now I have this one:

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  ;

xD One after another. Ok, I'm not sure if I should keep asking this in the same question, since the original problem is solved, therefore I'm accepting the answer and keep trying or opening a new question.


Solution

  • Here are a few things you can try

    1. Check if the service is running(browsing to the .svc file should be enough)
    2. Check if the service contracts are the same, this would be the WSDL files( check if all the methods are the same(more importantly if your method exists)