Search code examples
c#.nethttpnetwork-programminglaunchpad

How do I use request-response using HTTP libraries in C#.NET/Mono


Right now I have been trying to use Launchpad's API to write a small wrapper over it using C#.NET or Mono. As per OAuth, I first need to get the requests signed and Launchpad has it's own way of doing so.

What I need to do is to create a connection to https://edge.launchpad.net/+request-token with some necessary HTTP Headers like Content-type. In python I have urllib2, but as I glanced in System.Net namespace, it blew off my head. I was not able to understand how to start off with it. There is a lot of confusion whether I can use WebRequest, HttpWebRequest or WebClient. With WebClient I even get Certificate errors since, they are not added as trusted ones.

From the API Docs, it says that three keys need to sent via POST

  • auth_consumer_key: Your consumer key
  • oauth_signature_method: The string "PLAINTEXT"
  • oauth_signature: The string "&".

So the HTTP request might look like this:

POST /+request-token HTTP/1.1
Host: edge.launchpad.net
Content-type: application/x-www-form-urlencoded

oauth_consumer_key=just+testing&oauth_signature_method=PLAINTEXT&oauth_signature=%26

The response should look something like this:

200 OK

oauth_token=9kDgVhXlcVn52HGgCWxq&oauth_token_secret=jMth55Zn3pbkPGNht450XHNcHVGTJm9Cqf5ww5HlfxfhEEPKFflMqCXHNVWnj2sWgdPjqDJNRDFlt92f

I changed my code many times and finally all I can get it something like

HttpWebRequest clnt = HttpWebRequest.Create(baseAddress) as HttpWebRequest;
// Set the content type
clnt.ContentType =  "application/x-www-form-urlencoded";
clnt.Method = "POST";

string[] listOfData ={
    "oauth_consumer_key="+oauth_consumer_key, 
    "oauth_signature="+oauth_signature, 
    "oauth_signature_method"+oauth_signature_method
};

string postData = string.Join("&",listOfData);
byte[] dataBytes= Encoding.ASCII.GetBytes(postData);

Stream newStream = clnt.GetRequestStream();
newStream.Write(dataBytes,0,dataBytes.Length);

How do I proceed further? Should I do a Read call on clnt ?

Why can't .NET devs make one class which we can use to read and write instead of creating hundreds of classes and confusing every newcomer.


Solution

  • No, you need to close the request stream before getting the response stream.
    Something like this:

    Stream s= null;
    try
    {
        s = clnt.GetRequestStream();
        s.Write(dataBytes, 0, dataBytes.Length);
        s.Close();
    
        // get the response
        try
        {
            HttpWebResponse resp = (HttpWebResponse) req.GetResponse();
            if (resp == null) return null;
    
            // expected response is a 200 
            if ((int)(resp.StatusCode) != 200)
                throw new Exception(String.Format("unexpected status code ({0})", resp.StatusCode));
            for(int i=0; i < resp.Headers.Count; ++i)  
                    ;  //whatever
    
            var MyStreamReader = new System.IO.StreamReader(resp.GetResponseStream());
            string fullResponse = MyStreamReader.ReadToEnd().Trim();
        }
        catch (Exception ex1)
        {
            // handle 404, 503, etc...here
        }
    }    
    catch 
    {
    }
    

    But if you don't need all the control, you can do a WebClient request more simply.

    string address = "https://edge.launchpad.net/+request-token";
    string data = "oauth_consumer_key=just+testing&oauth_signature_method=....";
    string reply = null;
    using (WebClient client = new WebClient ())
    {
      client.Headers.Add("Content-Type","application/x-www-form-urlencoded");
      reply = client.UploadString (address, data);
    }
    

    Full working code (compile in .NET v3.5):

    using System;
    using System.Net;
    using System.Collections.Generic;
    using System.Reflection;
    
    // to allow fast ngen
    [assembly: AssemblyTitle("launchpad.cs")]
    [assembly: AssemblyDescription("insert purpose here")]
    [assembly: AssemblyConfiguration("")]
    [assembly: AssemblyCompany("Dino Chiesa")]
    [assembly: AssemblyProduct("Tools")]
    [assembly: AssemblyCopyright("Copyright © Dino Chiesa 2010")]
    [assembly: AssemblyTrademark("")]
    [assembly: AssemblyCulture("")]
    [assembly: AssemblyVersion("1.1.1.1")]
    
    namespace Cheeso.ToolsAndTests
    {
        public class launchpad
        {
            public void Run()
            {
                // see http://tinyurl.com/yfkhwkq
                string address = "https://edge.launchpad.net/+request-token";
    
                string oauth_consumer_key = "stackoverflow1";
                string oauth_signature_method = "PLAINTEXT";
                string oauth_signature = "%26";
    
                string[] listOfData ={
                    "oauth_consumer_key="+oauth_consumer_key,
                    "oauth_signature_method="+oauth_signature_method,
                    "oauth_signature="+oauth_signature
                };
    
                string data = String.Join("&",listOfData);
    
                string reply = null;
                using (WebClient client = new WebClient ())
                {
                    client.Headers.Add("Content-Type","application/x-www-form-urlencoded");
                    reply = client.UploadString (address, data);
                }
    
                System.Console.WriteLine("response: {0}", reply);
            }
    
            public static void Usage()
            {
                Console.WriteLine("\nlaunchpad: request token from launchpad.net\n");
                Console.WriteLine("Usage:\n  launchpad");
            }
    
    
            public static void Main(string[] args)
            {
                try
                {
                    new launchpad()
                        .Run();
                }
                catch (System.Exception exc1)
                {
                    Console.WriteLine("Exception: {0}", exc1.ToString());
                    Usage();
                }
            }
        }
    }