odata.net icon indicating copy to clipboard operation
odata.net copied to clipboard

How to add headers while making OdataClient call?

Open shagilt opened this issue 3 years ago • 11 comments

I have this client code to get list of movies from the server this.container.Movies.ExecuteAsync() Is there a way I can add some headers as part of this request?

I know we have DataServiceContext.SendingRequest2 hook to set header before we send the request, but I need to add different headers for different client. So, adding code in SendingRequest2 is not feasible.

shagilt avatar Aug 19 '20 18:08 shagilt

I wrote a extension to workaround this. Here is the extension code

public static async Task<IEnumerable<T>> ExecuteAsync<T>(this DataServiceQuery<T> query, Dictionary<string, string> headers)
        {
            EventHandler<BuildingRequestEventArgs> handler = null;
            query.Context.BuildingRequest += handler = (s, e) =>
            {
                //Unregister the event so that next query call will not use same headers.
                query.Context.BuildingRequest -= handler;
                if (headers != null && headers.Count > 0)
                {
                    foreach (var key in headers.Keys)
                    {
                        e.Headers.Add(key, headers[key]);
                    }
                }
            };

            return await query.ExecuteAsync<T>();
        }

shagilt avatar Aug 21 '20 23:08 shagilt

@shagilt Hi. What did you mean by "I need to add different headers for different client"? Do you mean you want to use different instances of DataServiceContext with different headers? Or did you mean different requests using the same DataServiceContext, but each request using its own headers?

habbes avatar Aug 25 '20 15:08 habbes

Hi @habbes,

Sorry that my question is not clear enough. I meant each request want to add different headers using same Microsoft.OData.Client.DataServiceContext. And above extension method is good for that.

But this question is still open for System.Data.Services.Client.DataServiceQuery. Is there a way to get System.Data.Services.Client.DataServiceContext from System.Data.Services.Client.DataServiceQuery?

shagilt avatar Sep 01 '20 01:09 shagilt

Hi @shagilt,

using DataServiceQuery<TElement> you can access the DataServiceContext using query.Context property.

Just to be clear, which version of the OData client are you using? The latest version of OData client is in the Microsoft.OData.Client namespace (i.e. Microsoft.OData.Client.DataServiceQuery<TElement> and Microsoft.OData.Client.DataServiceContext): https://docs.microsoft.com/en-us/dotnet/api/microsoft.odata.client?view=odata-client-7.0

I was also curious about the workaround that you provided. Is adding and removing event handlers in the manner you are doing thread-safe? Maybe if two requests are dispatched around the same time, is it possible that one may end up triggering an event handler of a different request before it gets removed? Or is it not a problem in your particular use-case?

habbes avatar Sep 08 '20 08:09 habbes

Hi @habbes

We have both OData V4 and V3 services. So, i need a solution for both :) Yes, i too have thread same concern with my above proposals. Do you have any other recommendation which is thread safe? As i mentioned, we need to set different headers for different requests.

shagilt avatar Sep 08 '20 17:09 shagilt

Hi @shagilt I haven't found a way for setting the headers per request other than the event handlers on the DataServiceContext, like SendingRequest2 and BuildingRequest. I guess it would have been better if the DataServiceQuery<T> provided methods to modify the request.

Maybe to prevent an event handler from executing for a request it was not meant for you could add a guard based on some condition that is specific to each query. For example, if the request uri is sufficient to distinguish headers that should be applied for the request you could have if (args.RequestMessage.RequestUri != query.RequestUri) return;

habbes avatar Sep 09 '20 12:09 habbes

Thanks @habbes . Could you please open a feature request for this? - it would have been better if the DataServiceQuery<T> provided methods to modify the request.

shagilt avatar Sep 09 '20 17:09 shagilt

@shagilt If the event handler was set on the DataServiceQuery, would that solve the problem completely? Are also requests issued from the same query use the same headers? From example, if you have:

context.Customers.ExecuteAsync(); // first request
context.Customers.Where(...).ExecuteAsync(); // second request
context.Customers.ExecuteAsync(); // third request

In your use-case, will those three requests use different sets of headers given that they use the same DataServiceQuery? Note that first and third requests would have the same URL. The second request would have different query options from the other two.

And what about POST/PATCH requests sent when you call context.SaveChangesAsync()?

habbes avatar Sep 15 '20 16:09 habbes

Our goal is to set headers for any ODataClient calls, that includes OdataServiceContext and POST/PATCH calls. Yes, these headers may be different for different calls. For example, caller wants to set a specific header for the first call, but for the second call, they might want to change it based of the response from first call.

In my opinion, ideal solution should: allow user to set custom headers for individual odata calls when they make Execute*() or Save() statement.

shagilt avatar Sep 15 '20 17:09 shagilt

Hi @habbes, could you give an update on the progress or outcome for this requested functionality if available?

In my particular use case I'm interested in setting the prefer header on a request when executing a DataServiceQuery.

Yglioh avatar Dec 20 '23 21:12 Yglioh

I'm stunned that it's confusing to add a to add custom headers to a request? It should be as standard as setting the request URI. Disappointing this hasn't made progress in over 3 years.

lenardchristopher avatar Feb 08 '24 16:02 lenardchristopher