Search code examples
c#.netrefit

Make Refit supply a parameter in the query string automatically


I'm using Refit to generate a client for a web service.

All the URLs of my Web API are like this:

https://service.com/api/v3/datasets?api_key=XXXXXXX

As you see, the API key is specified in the query string instead of the header.

I would like Refit to supply my access token automatically as part of the query string without having to specify it in my service interface.

I've looked into the documentation, but I haven't found a way to do it.


Solution

  • Finally, I created an HttpMessageHandler that intercepts the requests and injects the "api_key" in the querystring with the following code:

    Service creation

    service = RestService.For<IMyService>(new HttpClient(new QuerystringInjectingHttpMessageHandler(("api_key", token)))
        {
            BaseAddress = uri,
        });
    

    Handler

    public class QuerystringInjectingHttpMessageHandler : DelegatingHandler
    {
        private readonly (string Key, string Value)[] injectedParameters;
    
        public QuerystringInjectingHttpMessageHandler(params (string key, string value)[] injectedParameters) : this()
        {
            this.injectedParameters = injectedParameters;
        }
    
        public QuerystringInjectingHttpMessageHandler() : base(new HttpClientHandler())
        {
        }
    
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var finalUri = InjectIntoQuerystring(request.RequestUri, injectedParameters);
    
            request.RequestUri = finalUri;
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private static Uri InjectIntoQuerystring(Uri uri, IEnumerable<(string Key, string Value)> parameters)
        {
            var uriStr = uri.ToString();
    
            var queryString = new string(uriStr.SkipWhile(c => c != '?').ToArray());
            var baseUri = uriStr.Substring(0, uriStr.Length - queryString.Length);
            var currentParameters = HttpUtility.ParseQueryString(queryString);
    
            foreach (var (key, value) in parameters)
            {
                currentParameters[key] = value;
            }
    
            var tuples = currentParameters.ToTuples();
    
            var newUri =
                string.Join("&", tuples.Select(tuple =>
                {
                    if (tuple.name == null)
                    {
                        return tuple.value;
                    }
    
                    return tuple.name + "=" + tuple.value;
                }));
    
            var suffix = newUri == "" ? "" : "?" + newUri;
            var finalUri = new Uri(baseUri + suffix);
            return finalUri;
        }
    }
    

    Extension

    public static class Extensions
    {
        public static IEnumerable<(string name, string value)> ToTuples(this NameValueCollection collection)
        {
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }
    
            return
                from key in collection.Cast<string>()
                from value in collection.GetValues(key)
                select (key, value);
        }
    }