SharePoint Online, REST, and Windows Phone 8
April 7, 2013 at 3:50 am,
No comments
I recently found myself writing a SharePoint application for Windows Phone 8 which required me to be able to upload pictures to an individual's personal My Site (or SkyDrive Pro or OneDrive for Business or whatever it might be called next week). In doing so I ran into a number of issues and resulting solutions that I suspect others could benefit from, so I decided to share my experiences and code here.To start out I thought this would be easy... Ok, I was wrong on that, but I learned a lot in the process. My original app was pretty simple, take a photo and upload it to the users personal Document Library. To do this I needed:
1) Ability to login
2) Ability to locate the URL to their My Site
3) Upload the doc -- (I won't be covering this part in this posting...)
My original plan was to use the SharePoint SDK for Windows Phone. Login here was pretty easy and well documented, but the UserProfile library wasn't available on the mobile version, only the Win32 version. This meant I couldn't get the My Site Path that way. At that point I decided to switch to SharePoint REST, because I wouldn't need the CSOM library. But switching to REST meant I had to do the full OData authentication dance, which I wasn't excited about. I then discovered that while the standard Authenticator() object in the CSOM didn't allow you access to the cookies, the ODataAuthenticator() did. This made it easy to login using CSOM and then switch to REST for everything else.
====================================================================
static public string cookies;
static private Uri SiteURL;
static public void LoginO365(Uri SPSiteURL) // URL to your main SharePoint Online site
{
SiteURL = SPSiteURL;
ODataAuthenticator oat = new ODataAuthenticator();
oat.AuthenticationMode = ClientAuthenticationMode.MicrosoftOnline;
oat.AuthenticationCompleted +=
new EventHandler<AuthenticationCompletedEventArgs>(OnAuthenticationCompleted);
// The Authenticate method will raise the AuthenticationCompleted event.
oat.Authenticate(SPSiteURL);
}
static void OnAuthenticationCompleted(object sender, AuthenticationCompletedEventArgs e)
{
if (e.Error != null)
{
this.Dispatcher.BeginInvoke(delegate()
{
MessageBox.Show("The login attempt failed. Please try again");
});
return;
}
ODataAuthenticator oat = sender as ODataAuthenticator;
cookies = oat.CookieContainer.GetCookies(SiteURL);
this.Dispatcher.BeginInvoke(delegate()
{
NavigationService.Navigate(new Uri("/NextPage.xaml", UriKind.Relative));
});
}
=======================================================================
From here you can just strip off the cookies and pass them to your REST calls.
=======================================================================
public class RestResult
{
public string json { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
public async static Task<RestResult> RestCall(HttpWebRequest request)
{
request.Accept = "application/json;odata=verbose"; // JSON Result
request.Headers["Cookie"] = cookies["rtFa"] + "; " + cookies["FedAuth"];
HttpWebResponse response = await GetResponseAsync(request);
RestResult result = new RestResult();
result.StatusCode = response.StatusCode;
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
result.json = reader.ReadToEnd();
}
}
}
return result;
}
public static Task<HttpWebResponse> GetResponseAsync(HttpWebRequest request)
{
var taskComplete = new TaskCompletionSource<HttpWebResponse>();
request.BeginGetResponse(asyncResponse =>
{
try
{
HttpWebRequest aRequest = (HttpWebRequest)asyncResponse.AsyncState;
HttpWebResponse aResponse =
(HttpWebResponse)aRequest.EndGetResponse(asyncResponse);
taskComplete.TrySetResult(aResponse);
}
catch (WebException webExc)
{
HttpWebResponse failedResponse = (HttpWebResponse)webExc.Response;
taskComplete.TrySetResult(failedResponse);
}
}, request);
return taskComplete.Task;
}