A Simple Box.Com C# API Wrapper

August 25 2014

I had a need to access Box.com programmatically to do a daily pull of log files that were posted to Box from a third party service. At first I thought the Box .NET SDK would be helpful, but I quickly realized it is entirely oriented to be used by apps with a UI, not headless apps like this.  So, I dove into the documentation

My first stumble was that the Box developer token expires in one hour. I was hoping I’d be able to use it as a const for my headless server application, but no luck.

So, I need to actually generate an access_token and a refresh_token. The only way to do that is go through their UI. Thanks to a very helpful post on StackOverflow I was able to generate a code that could be used to generate both an access_token and refresh_token (which last for 60 days).

By persisting the refresh_token, you can write code that gets a fresh access_token programmatically. So, basically, my wrapper has a method called Bootstrap which you pass the code that you get from copy/pasting it out of the querystring. And then it has RefreshBoxToken, which gets a new access_token if the access_token is expired.

Then, there are two additional wrappers that actually do work which I called GetFolderByID and GetFileAsStream. GetFolderByID assumes you have the folderID, which you can figure out from the Box UI itself.  Then, with a little JSON.NET, you can parse the response and get the list of files as a JArray:

JObject jObject = JObject.Parse(folderContents);
JArray jArray = jObject["item_collection"]["entries"] as JArray;

Then, you’ve got the power do download files!

I wrapped both calls in a generic DoBoxAPICall method. Below is the entire class that encapsulates the logic:

using ExportConvivaLogsToHadoopWebJob.Properties;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace ExportConvivaLogsToHadoopWebJob
{

    public static class BoxAPIHelper
    {
        private const string boxApiUrl = "https://api.box.com/2.0/";
        private const string boxClientId = "YOUR_ID";
        private const string boxClientSecret = "YOUR_SECRET";
        private static readonly HttpClient _httpClient = new HttpClient();
        private static int retryCount = 0;
        private static Stream DoBoxCall(string url, HttpMethod httpMethod)
        {
            Stream stream;
            var request = new HttpRequestMessage() { RequestUri = new Uri(url), Method = httpMethod };
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Authorization", "Bearer " + Settings.Default.boxAccessToken);
            var response = _httpClient.SendAsync(request).Result;
            if (!response.IsSuccessStatusCode && response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                if (retryCount < 2)
                {
                    RefreshBoxToken();
                    retryCount++;
                    stream = DoBoxCall(url, httpMethod);
                    return stream;
                }
                else
                {
                    throw new Exception("Failed to connect to Box.");
                }

            }
            retryCount = 0;

            return response.Content.ReadAsStreamAsync().Result;
        }
        private static void RefreshBoxToken()
        {
            using (var request = new HttpRequestMessage() { RequestUri = new Uri("https://www.box.com/api/oauth2/token"), Method = HttpMethod.Post })
            {
                HttpContent content = new FormUrlEncodedContent(new[] 
                { 
                 new KeyValuePair<string, string>("grant_type", "refresh_token"), 
                 new KeyValuePair<string, string>("refresh_token", Settings.Default.boxRefreshToken),
                 new KeyValuePair<string, string>("client_id", boxClientId),
                 new KeyValuePair<string, string>("client_secret", boxClientSecret)
                
                }


                );
                request.Content = content;
                using (var response = _httpClient.SendAsync(request).Result)
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        throw new Exception("Box refresh token failed. A human needs to go to a browser and generate a fresh authorization code.");
                    }
                    JObject jObject = jObject = JObject.Parse(response.Content.ReadAsStringAsync().Result);
                    Settings.Default.boxAccessToken = (string)jObject["access_token"];
                    Settings.Default.boxRefreshToken = (string)jObject["refresh_token"];
                    Settings.Default.Save();
                }
            }



        }
        public static string GetFolderById(string folderId)
        {
            string url = string.Format("{0}folders/{1}", boxApiUrl, folderId);
            Stream stream = DoBoxCall(url, HttpMethod.Get);

            StreamReader reader = new StreamReader(stream);


            return reader.ReadToEnd();
        }
        public static void Bootstrap(string boxAccessCode)
        {
            using (var request = new HttpRequestMessage() { RequestUri = new Uri("https://www.box.com/api/oauth2/token"), Method = HttpMethod.Post })
            {
                HttpContent content = new FormUrlEncodedContent(new[] 
                { 
                 new KeyValuePair<string, string>("grant_type", "authorization_code"), 
                 new KeyValuePair<string, string>("code", boxAccessCode),
                 new KeyValuePair<string, string>("client_id", boxClientId),
                 new KeyValuePair<string, string>("client_secret", boxClientSecret)
                
                }


                );
                request.Content = content;
                var response = _httpClient.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    JObject jObject = jObject = JObject.Parse(response.Content.ReadAsStringAsync().Result);
                    Settings.Default.boxAccessToken = (string)jObject["access_token"];
                    Settings.Default.boxRefreshToken = (string)jObject["refresh_token"];
                    Settings.Default.Save();

                }

            }

        }
        public static Stream GetFileAsStream(string fileId)
        {
            string url = string.Format("{0}files/{1}/content", boxApiUrl, fileId);
            return DoBoxCall(url, HttpMethod.Get);
        }
    }
}

Maybe that’ll help someone out there…