How do i keep the Sharepoint context when moving around an ASP.NET MVC application without using the query string?

asp.net, asp.net-mvc, asp.net-mvc-4, c#, sharepoint

Solution

We had the same problem - ASP.NET MVC 4.5. There are two things that worked for us:

1) There is a spcontext.js file (included in the solution - you just have to add a reference to it) that will automatically append the tokens to the URL for you. However, we were given a requirement that the URL must look "nice," so we went with option 2..

2) Put the context in the session. Have the filter first look to see if you have the context in your session, and if it's there, then use that. If not, try the query string and put the retrieved context in your session. This means that you originally have to access your site with the tokens attached to your url string, and it also means that your context will be in the session for however long that's alive - so you have to decide if that's ok.

Problem

I'm building a small application in MVC 4.5. I've got an Azure database, and i'm using code first with the Entity framework to set it up. The app is hosted on my development sharepoint area. The Home controller's `Index()` Action has the `[SharePointContextFilter]` and loads, among other things, the username of the logged in user. When the application is debugged and this first action runs, the Sharepoint `{StandardTokens}` get appended to the url, so `SPHostUrl` and `AppWebUrl` and a few other variables get added to the query string. If i navigate away to an action without the `[SharePointContextFilter]` it works fine, until i navigate back to the action with the `[SharePointContextFilter]`. Then i get an error saying: ``` Unknown User Unable to determine your identity. Please try again by launching the app installed on your site. ``` I assume this is because a few of the Sharepoint `{StandardTokens}` are missing, because if i manually append them to the link like so: ``` @Url.Action("Index", "Home", new { SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Current.Request).AbsoluteUri }) ``` and mark the other action with the `[SharePointContextFilter]` as well, it still works. Hovever this seems like a needlessly complex way to solve this problem. I don't want to mark every single action in my app with the `[SharePointContextFilter]`, and manually insert the `{StandardTokens}` into the query string for every link i create. Shouldn't it be possible to save this information to session or a cookie in some way, so i don't have to do this? For reference, here is some code: HomeController.Index(), the first Action that is run. ``` [SharePointContextFilter] public ActionResult Index() { User spUser = null; var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext); using (var clientContext = spContext.CreateUserClientContextForSPHost()) { if (clientContext != null) { spUser = clientContext.Web.CurrentUser; clientContext.Load(spUser, user => user.Title); clientContext.ExecuteQuery(); ViewBag.UserName = spUser.Title; } } return View(); } ``` Here is the `[SharePointContextFilter]` attribute (generated by visual studio): ``` /// <summary> /// SharePoint action filter attribute. /// </summary> public class SharePointContextFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } SharePointContext currentContext = SharePointContextProvider.Current.GetSharePointContext(filterContext.HttpContext); Uri redirectUrl; switch (SharePointContextProvider.CheckRedirectionStatus(filterContext.HttpContext, out redirectUrl)) { case RedirectionStatus.Ok: return; case RedirectionStatus.ShouldRedirect: filterContext.Result = new RedirectResult(redirectUrl.AbsoluteUri); break; case RedirectionStatus.CanNotRedirect: filterContext.Result = new ViewResult { ViewName = "Error" }; break; } } } ``` The links that i use. From the _Layout.cshtml file.: ``` <li id="Home"><a href="@Url.Action("Index", "Home", new { SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Current.Request).AbsoluteUri })">Home</a></li> <li id="Contract"><a href="@Url.Action("Index", "Contract", new { SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Current.Request).AbsoluteUri })">Avrop</a></li> ``` If i try to use these links from an Action that isn't marked with the `[SharePointContextFilter]` filter, the `SPHostUrl` isn't found. If i try to link to an Action which is marked with the `[SharePointContextFilter]` filter, i get the aforementioned error if the `SPHostUrl` isn't included. This basically creates a situation where i can navigate away from the filtered actions, but then i can never return to them. I hope this was clear enough.

Original source