Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

So I'm trying to put together some very simple and elegant code samples to help people use my API. The latest language I'm tackling is C#.

I think the IETF OAuth2.0 standard I read implies the HTTP request Content-Type must be "application/x-www-form-urlencoded". The Django API server I have, currently seems to only support this Content-Type (for the OAuth resource). The other languages POST content this way by default!

After extensive research and several experiments I am wondering if I have missed something fundamental. Surely there would be a helpful library OR technique to create the ...urlencoded string OR at least someone else must have run into this???

I will outline some of the best solution I have so far bellow, but it just seems wrong.

Also from a bunch of internet browsing I figured I would use the HttpClient library. I like the fact that it uses the async model, which perhaps will be more useful for any developers using WPF or XAML or Windows 8 apps. It also works well for Consoles and Forms.

I use the Newtonsoft Json.Net library for serialization. First of all I create a POCO of the authorization strings. Then I serialize it to JSON, then to key/value pairs, then iterate through the key/value pairs catenating with the required '=' and '&' chars, then UTF-8, then escape the spaces etc.

//Setup HTTP request
HttpClient httpRequest = new HttpClient();
httpRequest.DefaultRequestHeaders.Add("Accept", "application/json");
string urlBase = "https://__secret__/api/v1/";
HttpResponseMessage msg = new HttpResponseMessage();

//POST to oauth to get token (must be sent as "application/x-www-form-urlencoded")
OAuthConfig oAuthCredentials = new OAuthConfig { client_id = client_id, client_secret = secret, username = "__secret__", password = "__secret__", grant_type = "__secret__" };
string jsonString = JsonConvert.SerializeObject(oAuthCredentials);  //convert to JSON
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonString);  //convert to key/value pairs
string urlEncodedData = ConvertToFormUrlEncodedFormat(values);
HttpContent payload = new StringContent(urlEncodedData, Encoding.UTF8, "application/x-www-form-urlencoded");
msg = httpRequest.PostAsync(urlBase + "oauth/access_token/", payload).Result;
string responseBodyAsText = msg.Content.ReadAsStringAsync().Result;
Console.WriteLine(responseBodyAsText);

Other options I could think of were...

Reflection, however digging into reflection in a code sample seems a bit manic.

It turns out that other than the OAuth setup, the rest of the API supports JSON type POSTs. So I guess I could write a function to walk through the oAuthConfig model and catenate a string, since that POCO model is unlikely to change and is the only model which requires urlEncoding that I can anticipate. That would save us from the slightly confusing use of the Dictionary. The use of the dictionary and then iterating is more generic, however perhaps a bit OTT.

I figure most of the time people will be using JSON, so showing how that might happen is useful.

In general it seems quite hard to serialize a POCO model to a string and/or to a urlEncoded string.

Questions

  • does OAuth mandate urlencoded, could you convert server-side?
  • is HttpClient the best choice, does async matter?
  • is there a better simpler way to serialize a POCO to ...form-urlencoded?

Thanks for any useful comments you may have.

share|improve this question

1 Answer 1

//I disclaimer everything and note that I haven't re-checked if this posted code works.

using System;
using System.Text;
using System.Collections.Generic;
using Newtonsoft.Json;  //install with Nuget package installer- "json.Net"
using System.Net.Http;  //install with Nuget package installer- "...Web API client libraries"
using System.Net;
using System.IO;
using System.Runtime.Serialization.Json;       //security risk till certificate fixed

namespace CSharpDemoCodeConsole
{
    class Program
    {
        const string api_key = "your_api_key"; //set your api_key here
        const string user_auth = "your_username" + ":" + "your_password"; // set your user credentials here
        const string urlBase = "https://@SECRET.com@/api/v1";

        static void Main(string[] args)
        {
            Console.WriteLine("Making call to webserver asynchronously");
            MakeCallAsynchronously();
            Console.WriteLine("**************************************");

            Console.WriteLine("Making call to webserver synchronously");
            MakeCallSynchronously();
            Console.WriteLine("**************************************");

            Console.WriteLine("Making call to webserver synchronously without Newtonsoft serialization");
            MakeCallSynchronouslyWithoutNewtonSoft();
            Console.WriteLine("Press spacebar to close the application");
            Console.ReadKey();
        }

        private static void MakeCallAsynchronously()
        {
            //Always accept untrusted certificates - don't use in production
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

            //Setup request
            string authorizeString = Convert.ToBase64String(Encoding.ASCII.GetBytes(user_auth));
            HttpClient httpRequest = new HttpClient();
            httpRequest.DefaultRequestHeaders.Add("Authorization", "Basic " + authorizeString);
            httpRequest.DefaultRequestHeaders.Add("Accept", "application/json");

            //GET from places resource
            try
            {
                var requestTask = httpRequest.GetAsync(urlBase + "places/" + "?api_key=" + api_key,
                System.Net.Http.HttpCompletionOption.ResponseContentRead);

                //Update UI while waiting for task to complete
                while (requestTask.Status != System.Threading.Tasks.TaskStatus.RanToCompletion)
                {
                    Console.Write(".");
                    System.Threading.Thread.Sleep(30);
                }
                if (requestTask.Result.StatusCode != HttpStatusCode.OK)
                {
                    Console.WriteLine("Unexpected response from server: {0}", requestTask.Result);
                    return;
                }
                var places = JsonConvert.DeserializeObject<Page<Place>>(requestTask.Result.Content.ReadAsStringAsync().Result);
                Console.WriteLine("GET places response " + requestTask.Result.Content.ReadAsStringAsync().Result);
            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            //POST to places resource
            try
            {
                string jsonString = JsonConvert.SerializeObject(new Place { name = "test place", latitude = 0, longitude = 0 });
                HttpContent payload = new StringContent(jsonString, Encoding.UTF8, "application/json");
                var requestTask = httpRequest.PostAsync(urlBase + "places/" + "?api_key=" + api_key, payload);

                //Update UI while waiting for task to complete
                while (requestTask.Status != System.Threading.Tasks.TaskStatus.RanToCompletion)
                {
                    Console.Write(".");
                    System.Threading.Thread.Sleep(30);
                }
                if (requestTask.Result.StatusCode != HttpStatusCode.Created)
                {
                    Console.WriteLine("Unexpected response from server: {0}", requestTask.Result);
                    return;
                }
                Console.WriteLine("POST places response " + requestTask.Result.Content.ReadAsStringAsync().Result);
            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }
        }

        private static void MakeCallSynchronously()
        {
            //Always accept untrusted certificates - don't use in production
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

            //Setup Request
            string authorizeString = Convert.ToBase64String(Encoding.ASCII.GetBytes(user_auth));
            var client = new WebClient();
            client.Headers.Add("Authorization", "Basic " + authorizeString);
            client.Headers.Add("Accept", "application/json");

            //GET from places resource
            try
            {
                var responseStream = client.OpenRead(urlBase + "places/" + "?api_key=" + api_key);
                var response = (new StreamReader(responseStream).ReadToEnd());
                var places = JsonConvert.DeserializeObject<Page<Place>>(response);
                Console.WriteLine("GET places response " + response);
            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
            }

            //POST to places resource
            try
            {
                client.Headers.Add("Accept", "application/json");
                client.Headers.Add("Content-Type", "application/json");
                string jsonString = JsonConvert.SerializeObject(new Place { name = "test place", latitude = 0, longitude = 0 });
                client.Encoding = System.Text.Encoding.UTF8;
                string response = client.UploadString(urlBase + "places/" + "?api_key=" + api_key, jsonString);
                Console.WriteLine("POST places response " + response);
            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }
        }

        private static void MakeCallSynchronouslyWithoutNewtonSoft()
        {
            //Always accept untrusted certificates - don't use in production
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

            //Setup Request
            string authorizeString = Convert.ToBase64String(Encoding.ASCII.GetBytes(user_auth));
            var client = new WebClient();
            client.Headers.Add("Authorization", "Basic " + authorizeString);
            client.Headers.Add("Accept", "application/json");

            //GET from places resource
            try
            {
                var responseStream = client.OpenRead(urlBase + "places/" + "?api_key=" + api_key);
                MemoryStream ms = new MemoryStream();
                responseStream.CopyTo(ms);
                ms.Position = 0;
                var placesDeserializer = new DataContractJsonSerializer(typeof(Page<Place>));
                var places = (Page<Place>)placesDeserializer.ReadObject(ms);
                ms.Position = 0;
                string response = (new StreamReader(ms).ReadToEnd());
                ms.Close();
                Console.WriteLine("GET places response " + response);
            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }

            //POST to places resource
            try
            {
                client.Headers.Add("Accept", "application/json");
                client.Headers.Add("Content-Type", "application/json");
                DataContractJsonSerializer placesSerializer = new DataContractJsonSerializer(typeof(Place));
                Place place = new Place { name = "test place", latitude = 0, longitude = 0 };
                MemoryStream ms = new MemoryStream();
                placesSerializer.WriteObject(ms, place);
                byte[] json = ms.ToArray();
                ms.Close();
                string jsonString = Encoding.UTF8.GetString(json, 0, json.Length);
                client.Encoding = System.Text.Encoding.UTF8;
                string response = client.UploadString(urlBase + "places/" + "?api_key=" + api_key, jsonString);
                Console.WriteLine("POST places response " + response);

            }
            catch (WebException ex)
            {
                Console.WriteLine(ex.ToString());
                return;
            }
        }
    }

    public class Place
    {
        [JsonProperty("url")]
        public string url { get; set; }
        [JsonProperty("name")]
        public string name { get; set; }
        [JsonProperty("latitude")]
        public float latitude { get; set; }
        [JsonProperty("longitude")]
        public float longitude { get; set; }
    }

    public class Page<T>
    {
        [JsonProperty("count")]
        public int count { get; set; }
        [JsonProperty("next")]
        public string next { get; set; }
        [JsonProperty("previous")]
        public string previous { get; set; }
        [JsonProperty("results")]
        public List<T> results { get; set; }
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.