Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot pass MVC5 Request.QueryString or Request.Headers to Auth service methods. #164

Closed
nozzlegear opened this issue Aug 14, 2017 · 4 comments

Comments

@nozzlegear
Copy link
Owner

nozzlegear commented Aug 14, 2017

In ASP.NET MVC5 the Request.QueryString and Request.Headers objects are of type System.Collections.Specialized.NameValueCollection - a type that does not exist in .NET Standard 1.4. In the guides I've written, and even in the ShopifySharp documentation, we tell users to pass those objects to the various AuthorizationService.IsValidX functions, but in v4+ those functions now expect a list of KeyValuePair<string, StringValues>.

For those reading this issue wondering how to get around this, for now you can create this extension method and then call Request.QueryString.ToKvps() or Request.Headers.ToKvps() and pass the result to the auth methods:

namespace MyNamespace
{
  public static class Extensions
  {
    public List<KeyValuePair<string, StringValues>> ToKvps(this System.Collections.Specialized.NameValueCollection qs)
    {
      Dictionary<string, string> parameters = qs.Keys.Cast<string>().ToDictionary(key => key, value => qs[value]);
      var kvps = new List<KeyValuePair<string, StringValues>>();
      
      parameters.ToList().ForEach(x =>
      {
        kvps.Add(new KeyValuePair<string, StringValues>(x.Key, new StringValues(x.Value)));
      });
      
      return kvps;
    }
  }
}

I'd like to explore fixing this in the package without adding an extra dependency. I'm not sure that's possible though, because even including the above extension method in ShopifySharp would require a reference to the type which simply isn't available.

@bentolila
Copy link

Great work around.

I was able to do this work-around by doing the following as well. :)

Request.QueryString.Keys.Cast<string>().ToDictionary(k => k, v => (StringValues)Request.QueryString[v]);

Request.Headers.Keys.Cast<string>().ToDictionary(k => k, v => (StringValues)Request.Headers[v]);

@vinhch
Copy link

vinhch commented Aug 16, 2017

I have this can work too :D

public static IEnumerable<KeyValuePair<string, StringValues>> ToKVPairs(this NameValueCollection collection)
{
    if (collection == null)
    {
        throw new ArgumentNullException(nameof(collection));
    }

    return collection.Cast<string>().Select(key => new KeyValuePair<string, StringValues>(key, collection[key]));
}

@nozzlegear nozzlegear added question and removed bug labels Dec 11, 2017
@nozzlegear
Copy link
Owner Author

I've added a couple additional overloads to IsAuthenticRequest: you can now pass a Dictionary<string,string>, or more simply the raw querystring string.

@RichardD2
Copy link

A querystring can contain multiple items with the same key - for example ?foo=bar&foo=baz. Any method which converts the collection to a dictionary will fail in this case.

It would also be better to avoid using LINQ for something this simple. And since you only need an IEnumerable<T>, you could use an iterator method to avoid allocating a new List<T>.

public static IEnumerable<KeyValuePair<string, StringValues>> ToKvps(this NameValueCollection source)
{
    if (source is null) throw new ArgumentNullException(nameof(source));
    return Iterator(source);
    
    IEnumerable<KeyValuePair<string, StringValues>> Iterator(NameValueCollection qs)
    {
        foreach (string key in qs.Keys)
        {
            string[] values = qs.GetValues(key);
            yield return new KeyValuePair<string, StringValues>(key, values);
        }
    }        
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants