LBD: C# Basic Authentication to SubSonic API

The Problem

I have decided that I want to write a C# wrapper around the Subsonic API. Should be easy enough, but as with all things whilst I still have my programming “training wheels” back on at the moment there are some challenges. The first one being how to call the API using Basic Authentication?

The API does provide the option of passing the username and password in the query string:

http://example.com/rest/ping.view?u=user&p=password&v=1.10.2&c=MyApp

I really don’t like the thought of passing the unencrypted credentials with every call to the server so I want to implement the Basic Authentication option.

The Solution

The idea of these posts was to write down everything that I did to get to the solution to show people all of the pitfalls they might have. However, in this case I tried so many different things before I got a working solution it just isn’t feesible to put them all in a single post that isn’t about 3000 words long! So this is the abridged version.

The first thing I did was ask the question “How do I call a web service using Basic Authentication?” The first answer from Google was from Stackoverflow.

Using this information I came up with the following ‘Spike’ test to check I could get the response I was looking for from the server:

        public void CallApi()
        {
            try
            {
                var url = "http://myserver.com:0000/rest/ping.view?v=1.10.2&c=MyApp";
                NetworkCredential creds = new NetworkCredential("username", "password");

                WebRequest request = WebRequest.Create(url);
                request.Credentials = creds;

                // Get response
                using (var response = request.GetResponse())
                {
                    var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                }
            }
            catch (WebException Ex)
            {

            }
        }

Very quick, very dirty, not my best work, but I was just trying to prove the concept before I go trying to setup the whole project and start implementing all of the API calls.

Unfortunately this didn’t work. I was getting an error message with the value 10 returned indicating a required parameter was missing, as per the API documentation.

This is the point where I lost track of all the things I was trying. I knew it had to be the passing of the credentials as when I added them to the query string of the call and ran the test it worked perfectly and returned OK for the status.

Eventually I stumbled across a post from a Subsonic Developers Google Group from a few years ago. It had a sample in VB which I tested out and worked perfectly. From looking at the code it was the way I passed the credentials with the request. From what I read about using NetworkCredentials it would encode the string and pass it in the headers, but I couldn’t get it to work. This manual method of encoding and then adding to the headers worked so I went with it.

After a bit of cleaning up I now have the following:

public class ServerConfiguration
    {
        public string ServerUrl { get; set; }

        public int Port { get; set; }

        public string RestApi { get; set; }

        public string ApplicationName { get; set; }

        public string UserName { get; set; }

        public string Password { get; set; }
    }
public class SubsonicResponse
    {
        public string Status { get; set; }

        public string Version { get; set; }

        public string Xmlns { get; set; }
    }

 public class ApiCalls
    {
        private readonly ServerConfiguration _server;

        public ApiCalls(ServerConfiguration serverConfiguration)
        {
            _server = serverConfiguration;
        }

        public string Call(string call)
        {
            var jsonString = string.Empty;
            try
            {
                var callUrl = string.Format("{0}:{1}/rest/{2}?v={3}&c={4}&f=json",
                                            _server.ServerUrl,
                                            _server.Port, call,
                                            _server.RestApi,
                                            _server.ApplicationName);

                var request = WebRequest.Create(callUrl);
                request.Headers["Authorization"] = "Basic " + EncodeCredentials(_server.UserName, _server.Password);

                // Get response
                using (var response = request.GetResponse())
                {
                    jsonString = new StreamReader(response.GetResponseStream()).ReadToEnd();
                    var json = JObject.Parse(jsonString);
                    var status = JsonConvert.DeserializeObject<SubsonicResponse>(json["subsonic-response"].ToString());
                    if (status.Status != "ok")
                    {
                        //handle error here
                    }
                }
            }
            catch (WebException Ex)
            {
                //handle error
            }
            return jsonString;
        }
}

It’s still not the prettiest code, but it works and I can refine the error handling part as I go.

I can pass the API call to this method and it will request a JSON result from the Subsonic server. I can then deserialize this using the Newtonsoft.Json library into strongly typed objects. Well that’s the plan, I haven’t managed to get any further than just checking the ‘Ping’ command works so I am a long way to finished.

Conclusion

This looks like an easy fix, and it was. It just took me a long time to find it. I am sure there must be a way to make it work using NetworkCredentials and a credential cache as all the documentation says it is meant to work for Basic Authentication. I just haven’t figured it out yet.

The next problem is how to unit test this, which will be the topic of the next post.