c# Build URL encoded query from model object for H

2020-03-26 03:19发布

问题:

Need to build URL encoded query from model object for HttpClient

My Model is

class SaveProfileRequest
{
    public string gName { get; set; }
    public string gEmail { get; set; }
    public long gContact { get; set; }
    public string gCompany { get; set; }
    public string gDeviceID { get; set; }
    public string Organization { get; set; }
    public string profileImage { get; set; }
    public string documentImagefront { get; set; }
    public string documentImageback { get; set; }
}

SaveProfileRequest request = new SaveProfileRequest() { gName = name, gEmail = email, gContact = phonenumber, gCompany = company,
            gDeviceID = deviceId, Organization = "", profileImage = "", documentImageback = "", documentImagefront = "" };

string response = await RequestProvider.PostAsync<string, SaveProfileRequest>(uri, request);

I have a working code for content type JSON

content = new StringContent(JsonConvert.SerializeObject(data));

content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

Where data is of type TInput

Tried

content = new StringContent(System.Net.WebUtility.UrlEncode(JsonConvert.SerializeObject(data)));

content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

but didn't workout

回答1:

JsonConvert produces only json content. For urlencoded query you should construct instance of FormUrlEncodedContent. As constructor parameter it takes collection of KeyValuePair<string, string>. So the main trick is to build this collection for model object.

You could use reflection for this purpose. But there is a simpler solution based on Json.net. It was described here and following ToKeyValue() extension method is a copy/paste from that blog post:

public static class ObjectExtensions
{
    public static IDictionary<string, string> ToKeyValue(this object metaToken)
    {
        if (metaToken == null)
        {
            return null;
        }

        JToken token = metaToken as JToken;
        if (token == null)
        {
            return ToKeyValue(JObject.FromObject(metaToken));
        }

        if (token.HasValues)
        {
            var contentData = new Dictionary<string, string>();
            foreach (var child in token.Children().ToList())
            {
                var childContent = child.ToKeyValue();
                if (childContent != null)
                {
                    contentData = contentData.Concat(childContent)
                        .ToDictionary(k => k.Key, v => v.Value);
                }
            }

            return contentData;
        }

        var jValue = token as JValue;
        if (jValue?.Value == null)
        {
            return null;
        }

        var value = jValue?.Type == JTokenType.Date ?
            jValue?.ToString("o", CultureInfo.InvariantCulture) :
            jValue?.ToString(CultureInfo.InvariantCulture);

        return new Dictionary<string, string> { { token.Path, value } };
    }
}

Now you could build the url-encoded content as easy as:

var keyValues = data.ToKeyValue();
var content = new FormUrlEncodedContent(keyValues);


回答2:

This can be done effortlessly with Flurl (disclaimer: I'm the author) as this exact scenario - parsing a .NET object's properties into URL-encoded key-value pairs - is supported right out the box:

using Flurl.Http;

var resp = await url.PostUrlEncodedAsync(data);

Flurl uses HttpClient under the hood, so in the example above, resp is an instance of HttpResponseMessage, just as if the call was made using HttpClient directly. You could also append .ReceiveString() to the call if you just want the response body, or .ReceiveJson<T>() if you're expecting a JSON response and want a matching .NET object of type T.



回答3:

You can use this simplified version

public static class URLExtensions
{
    public static string ToKeyValueURL(this object obj)
    {
        var keyvalues = obj.GetType().GetProperties()
            .ToList()
            .Select(p => $"{p.Name} = {p.GetValue(obj)}")
            .ToArray();

        return string.Join('&',keyvalues);
    }
}