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…