simple-odata-client / Simple.OData.Client

MIT License
331 stars 197 forks source link

OData protocol is not support exception #602

Closed tvhars closed 5 years ago

tvhars commented 5 years ago

I receive the following stack dump (below) regardless of the methods used to query the oData source (untyped, dynamic, or typed). The oData source (v2) for the query returns correct data when queried direct with a REST call programmatically or via a browser. The query also executes correctly on the testing instance of the system.

Is there any additional debug or information the simple.odata classes can return to narrow down further the source of the problem? Is this a general exception or does it have a specific meaning? As the testing against the source shows you can query correctly as an oData source and therefore the odata protocol should be supported.

Many thanks.

--- DUMP ---

Unhandled Exception: System.NotSupportedException: OData protocol is not supported at Simple.OData.Client.AdapterFactory.CreateModelAdapter(String metadataString, ITypeCache typeCache) at Simple.OData.Client.AdapterFactory.CreateAdapter(String metadataString, ITypeCache typeCache) at Simple.OData.Client.EdmMetadataCache..ctor(String key, String metadataDocument, ITypeCache typeCache) at Simple.OData.Client.Session.<>cDisplayClass26_0.<b0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.EdmMetadataCache.d5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.Session.d26.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.Session.d17.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.Session.d20.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.FluentCommand.d20.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Simple.OData.Client.BoundClient`1.d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) at CA_SOD.CA_SOD_Prg.d2.MoveNext() in C:\Source\repos\CRM\SampleCode\CA_EDMLibSample\CA_SOD_Prg\CA_SOD_Prg.cs:line 65 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at CA_SOD.CA_SOD_Prg.

d__1.MoveNext() in C:\Source\repos\CRM\SampleCode\CA_EDMLibSample\CA_SOD_Prg\CA_SOD_Prg.cs:line 33 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at CA_SOD.CA_SOD_Prg.
(String[] args)

phatcher commented 5 years ago

At a guess you are using the V4 Adapter rather than the V3 Adapter which supports OData v1-3.

Check which assembly you are referencing and lets see the startup code

tvhars commented 5 years ago

Thanks for the quick reply - unfortunately not as simple as the wrong assemblies :-( attached file shows pict of current assemblies definitely the V3 adaptor.

references

Test rig code below -

tstRig.txt

object commented 5 years ago

Can you paste your service metadata document?

tvhars commented 5 years ago

Hopefully this is the information you want - attached : C4C-metadata-xml.txt.

Also, I added the OnTrace to the code for the instantiation of the oDataClienttSettingDetails class with the following returned information if this is helpful -

DYNAMIC READ Service Ticket
GET request: https://my323585.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/$metadata
Request completed: OK
OData protocol  is not supported 
(caught exception text)```

Are there any options which generate a deeper level of trace information?
I wrote the following 'raw' query and it seems to return the correctly formatted oData payload (again not sure if this helps except showing that the server does return data when queried) - 

var username = "xxxx"; var password = "xxxx"; var authHeader = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(username + ":" + password)); var cookieContainer = new CookieContainer(); // Connection End // Product File Creation // Get request for reading the list of collections HttpWebRequest request = (HttpWebRequest)(WebRequest.Create("https://myxxxxxx.crm.ondemand.com/sap/c4c/odata/v1/c4codataapi/ServiceRequestCollection"));

// Share cookies between the requests request.CookieContainer = cookieContainer; // Set auth header request.Headers.Add("Authorization", authHeader); // Set header to fetch a CSRF token to be used in modifying requests request.Headers.Add("x-csrf-token", "fetch"); // Set accept header to get the data in JSON. Default response format is XML //request.Accept = "application/json"; // Get the response.
HttpWebResponse response = (HttpWebResponse)(request.GetResponse()); // Get the stream containing content returned by the server.
using (Stream dataStream = response.GetResponseStream()) { // Open the stream using a StreamReader for easy access.
using (StreamReader reader = new StreamReader(dataStream)) { // Read the content.

    string responseFromServer = reader.ReadToEnd();
    // Now Write the contents to a BLOB file

    // write a blob to the container, add to Header add Footer
    Console.WriteLine(responseFromServer);

    // Get x-csrf-token
    string x_csrf_token = response.Headers["x-csrf-token"];
    // Clean up the streams and the response.  
    response.Close();
    // Clean up the streams.  
    reader.Close();
}
dataStream.Close();

}



[C4C-metadata-xml.txt](https://github.com/simple-odata-client/Simple.OData.Client/files/3129509/C4C-metadata-xml.txt)
[Vendor Doco](https://help.sap.com/doc/d0f9ba822c08405da7d88174b304df84/LATEST/en-US/index.html#/topic/ServiceRequest)
tvhars commented 5 years ago

C4C-metadata-xml.zip

Not sure that last file attachment worked - resent

object commented 5 years ago

Your schema is correct and is read properly. I wrote a simple test:

        [Fact]
        public async Task Test()
        {
            using (var reader = new StreamReader(@"F:\Temp\C4C-metadata.xml"))
            {
                var metadataDoc = await reader.ReadToEndAsync();
                var settings = new ODataClientSettings { BaseUri = _serviceUri, MetadataDocument = metadataDoc };
                var client = new ODataClient(settings);

                var doc = await client.GetMetadataAsync();
            }
        }

So the error must be somewhere else, possibly in the content of your DLLs. A possible way to uncover it is to build the library from sources and debug through its code to find out what exactly happens.

tvhars commented 5 years ago

Thank you for the help and suggestions. I have traced the mechanics of the request and response (against the working system instance and problematic one) - it appears that the authentication for the one not working is presenting a login page back for credentials and therefore not what is being expected - almost like the sent credential information is not being seen by the server and therefore the default solution login page is being generated as the return request.

Even with full tracing on for both requests and response I don't see what is being passed as the message header contents. I tried to trace this during the execution but the headers (Simple.OData.Client.Core/ODataRequest.cs line 62 - 80) does not have any header information populated at this point during the execution.

Is there a way to dump the request header information prior to sending (or a place in the code where this should be set prior to sending to the server for processing?) I can see the rest of the send payload and it seems to be fine - I think the header will also be fine but just want to investigate further to help resolve the issue.

many thanks.

phatcher commented 5 years ago

@tvhars You are responsible for setting any authentication headers, there are a couple of approaches...

  1. Setting a delegate on the ODataClientings.BeforeRequestAsync
  2. Inject a HttpClient into the the ODataClient with an appropriate DelegatingHandler to assign the header

I'm generally using the first approach in .NET Framework and the second in .NET Core.

tvhars commented 5 years ago

Would you be able to share or point me in the direction of some sample code for the methods mentioned above? as guide - thanks.

tvhars commented 5 years ago

In trying the second approach (as the code utilises the .net core libraries). Do you inject the HttpClient object into the oDataClientSettings at object creation, and if so, pre-populate the object with the header values.
Or is the HttpClient object injected into a different oData object?
Adding the object through the oDataClientSettings appears to result in a null referenced object not the populated HttpClient created.

I am obviously missing a step/nuance of the second option??

tvhars commented 5 years ago

I think I have worked something out for this item - thank you for the assistance.

banditoth commented 2 years ago

Note for developers getting this error This exception usually thrown when the Simple.OData.Client tries to get the metadata of your webservice, but instead of getting the metadata, it redirects the HttpClient to a SSO login form instead. Make sure that you've set the authentication correctly.