Monday 17 September 2012

SharePoint apps: working with the app web, and why you should

Disclaimer: Just because I’m writing about apps, doesn’t mean I think everything in SP2013 should be developed as an app! I’m focusing on apps simply because it’s a deep area with lots to learn.

  1. SharePoint 2013 apps – architecture, capability and UX considerations
  2. Getting started – creating lists, content types, fields etc. within a SharePoint app (provisioning)
  3. Working with data in the app web, and why you should [this article]
  4. Access end-user data (in the host web) from a SharePoint 2013 app
  5. Rolling out SharePoint 2013 apps to the enterprise - tenant scope and PowerShell installs
  6. Azure is the new SharePoint ‘_layouts’ directory
  7. “Host web apps” – provisioning files (e.g. master pages) to the host web
  8. ““Host web apps” – provisioning fields and content types
  9. Deploying SP2013 provider-hosted apps/Remote Event Receivers to Azure Websites (for Office 365 apps)
  10. Working with web parts within a SharePoint app

The app web is the default

You might already have come across the idea that SharePoint 2013 apps which host SharePoint components (e.g. lists, content types etc.) put these into an isolated area called the app web. If not, my previous article Creating lists, content types and fields within a SharePoint app (provisioning) provides some details, including some explanation around the diagram below, which summarises an app I’m building:

TimeTrackingApp

The main thing to consider about the app web is that it is the default – if you write some Client Object Model (CSOM) code (remember that SharePoint server-side code is forbidden within an app), perhaps to find a list and retrieve the items, in the standard app arrangement it is the app web you are working with. This is because, by default, you’ll be in a page within the app web – meaning your context is the app web, and that’s what is reflected in the ClientContext object you’ll be working with in your code. What happens is that when you go through an app’s “front door” in the Site Contents area, you are redirected to a page in the app web – and the ClientContext object in the CSOM is simply doing what it always does i.e. obtaining the current web as per the URL in the browser address bar.

Consequently, there is nothing special needed to ‘get a reference’ to the app web in your code. As a separate topic, the same cannot be said of the “host web” (the “real” SharePoint site where the app was installed) – in a best practice configuration which has the app domain on a completely separate URL domain to team sites/My Sites etc. (e.g. “http://myCorp-apps.com” vs. “http://myCorp.com” ), you’ll need to use the SP.RequestExecutor cross-domain library which Microsoft supply to access the host web. More on this in the "Working with data in the host web” article later in this series.

Getting the URLs for the app web and host web (within an app)

Within an app, the developer needs to know the URL for the app web and the host web where the app is installed.  The relevant URLs for the app web and the host web are passed as querystring parameters in the URL through to the first page load of your app, when the user arrives there from the app entry point in the Site Contents area. In fact, any time the user hits the home page of your app (e.g. by clicking the breadcrumb link within the app itself), you’ll see them in the browser address bar. So, it’s simply a matter of reading these URL  parameters – there are lots of ways to do this in JavaScript, I use the following jQuery plugin but anything similar will be fine; simply ensure it’s in a JavaScript file referenced by your app pages:

// jQuery plugin for fetching querystring parameters..
jQuery.extend({
    getUrlVars: function () {
        var vars = [], hash;
        var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
        for (var i = 0; i < hashes.length; i++) {
            hash = hashes[i].split('=');
            vars.push(hash[0]);
            vars[hash[0]] = hash[1];
        }
        return vars;
    },
    getUrlVar: function (name) {
        return jQuery.getUrlVars()[name];
    }
});

Then to grab the app web/host web URLs, you would do:

// retrieve passed app web/host web URLs..
hostweburl = decodeURIComponent($.getUrlVar("SPHostUrl"));
appweburl = decodeURIComponent($.getUrlVar("SPAppWebUrl"));

BEWARE: bug in SharePoint 2013 Preview around the host web URL

One thing I notice in the current build (Preview), is that the host web URL only comes through on the first-time page load (app entry) from the host web. When the user is navigating around any pages you create in the app web, if they click on the breadcrumb link to go to the app’s default page, the URL parameter is present but has an incorrect value. It now has the URL  of the app web, instead of the host web. Consequently if you’re trying to look up something in the host web, this code will fail at this point. I’m assuming this has to be a bug, and will file it on Connect very soon (it hadn’t already been logged when I checked).

As a temporary workaround, I’m persisting the value in a cookie on first-time entry, then fetching it from there as needed. This works fine, I’d be happy to share my code if useful to anyone (just leave me a comment below), but it’s not particularly complex and I’m sure you can imagine what it does.

Common tasks with the app web

So if it’s just standard Client Object Model code and nothing special is required to ‘get a reference’ to the app web, then standard JavaScript CSOM code samples are all you need to help you along. As such, a previous article of mine has some basic JavaScript CSOM examples that I often find myself referring to when I can’t remember syntax and whatnot:

Other good sources are:

Remember that the JavaScript version of the Client Object Model (which I’m discussing here) is what you’d use in a web page, but if you were in server-side code of some sort (Windows desktop app/Windows Phone app, WCF service, PowerShell script, console app etc.) then you could use the .NET Managed Client Object Model and code in C# for example.

Using the app web vs. the host web

So, working with the app web is pretty simple. In security terms, the app principal (the identity of the app – this is a fully-fledged security identity) has Full Control permission of the app web, so you can accomplish anything you need to, so long as it can be done with client-side code. However, as I pointed out last time, the app web is pretty sparse by default – there’s no real navigation, many of the SharePoint system pages cannot be used (e.g. the Site Contents page, /_layouts/viewlsts.aspx) and obviously there’s a separation between the app and the end-user’s collaboration stuff (lists and libraries) which is back in the host web. Obviously you could customise heavily and maybe even get to something like that feels like a team site, but it would be quite a lot of work – it’s not permitted to actually use a template such as a team site (STS#0) for an app web. Here’s a reminder of what that default user experience looks like:

AppDefaultPage 

If this isolation of the app doesn’t really suit the requirements, an alternative arrangement could be for the app to provision it’s lists/pages/etc. into the host web. To be clear, this is actually isn’t possible in many scenarios for two main reasons:

  • Security – the app cannot mess around with the host web; by default it has very limited read permissions only. This, of course, is the whole point of the isolation and a key benefit of apps for whoever has to plan, maintain and upgrade the SharePoint deployment
  • Provisioning model – if you add a Feature to your Visual Studio project to create, for example, a list – then this list gets created in the app web. It is simply not possible to use traditional provisioning approaches against the host web from within an app

However, there is a combination of circumstances where it is possible:

  • The app comes with a permission request for Full Control to the host web (which must be granted by the user installing the app for the app to install)
  • Provisioning is implemented via the Client Object Model (CSOM)

These constraints mean it might not always be possible to use this approach. For example, Microsoft is unlikely to ever approve an app which requires Full Control of the host web for use in Office 365/SharePoint Online. Similarly, SharePoint administrators deciding whether a publicly-available app is safe to be added their organisation’s App Catalog would most likely take the same view. BUT, it might make sense to fulfil a particular client requirement – if a client wants a certain set of functionality and wants it implemented as an app, then this might be the way.

Doug Ware, a fellow SharePoint MVP and true “developer’s developer”, is doing a lot of thought leadership in this area. The links at the bottom of this article are to his posts, which I think are required reading if you believe this approach is relevant to you. To build an app of this type, it quickly becomes obvious that a library of CSOM provisioning methods (e.g. createField(), createContentType(), createList(), addListItem(), addFile() and so on) is necessary, and Doug is starting a blog series on this topic.

However, I don’t believe this approach is a panacea for challenges around the app model. Here’s why…

…implementing an app in the host web might not be a good idea

So we’ve already covered the constraints which could mean it’s not possible in the first place (the need for Full Control permissions in the host web). I also think that:

  • The host web approach partly defeats the purpose of apps (a very specific piece of functionality, which runs isolated from the SharePoint environment so there’s no negative impact to platform stability, any future upgrades and user data) 
    • If the app is just for one client, I feel like the benefits of apps are eroded so much you may as well build a farm solution. It will be way quicker, no matter how good you are with JavaScript and the CSOM.
  • For many requirements, putting the artifacts in the app web should actually work just fine. If you need to store some list/library data and provide some pages for a front-end, then the fact that this is somewhat separate from the end user’s core data doesn’t really matter.
    • For example, in the somewhat contrived time-tracking app that I’m building, so long as the user interface makes it quick and easy to see how many hours the current user has logged this week, and what his/her target is - I don’t think he/she would care whether the data was coming from a list in the app or host web.
  • I’m convinced Microsoft don’t want you to build apps in the host web. I’m mainly basing this on the following observations:
    • The provisioning model is all around the app web
    • It’s not possible to set the app’s start page to be a page in the host web. Notably, all the URL tokens which can be used at the beginning of the start page URL are for the app web (or remote app web in the case of an externally-hosted app). See the MSDN page URL strings and tokens in apps for SharePoint and note the “This token cannot be used in the StartPage element of an app manifest…” statements next to each token which refers to a host web item
    • No SDK samples use this approach
    • No MSDN documentation discusses it (at the time of writing at least)
    • Many of their design goals of the app web would not be met (security, reduction in upgrade complexity for customized environments, etc.)

Effectively, I almost see the idea of an app requesting Full Control permission and then provisioning there as a LOOPHOLE.

Summary

So does that mean that we should never use this approach (and that Doug is wrong)? Do I think that we have to build solutions exactly as Microsoft envisage? Hell no! For starters, Microsoft say that every customization should be built as an app in SharePoint 2013, and as my disclaimer at the beginning of these articles says, I personally don’t agree with that. 

But I do think you should think long and hard before using the “exploitable loophole” of building in the host web. Too many devs fail to see the full Total Cost of Ownership of their solutions - around maintainability (and skills/experience/capability required), long-term stability, performance, re-work required during upgrade and so on - because they’ve long since moved onto the next project/client. I’ve seen extremely impressive solutions (created by very talented devs) slide from being absolutely loved to being a big organizational headache within months. Of course, the original dev wasn’t around to see this – if he was, he probably could have managed perceptions, minimized the problem and perhaps learnt a lesson - but he wasn’t. It’s hard to accept, but some of my solutions have probably gone in this direction too. Now I’m not suggesting Doug is guilty of this – I’m sure someone of that brainpower is able to look after his SharePoint clients’ best interests very well – but there is a  long-term impact around SharePoint dev work which implementers should consider. And I feel that if the app model is suitable in the first place, then implementing an app in the host web (rather than the app web) may be part of the problem, rather than part of the solution.

All this isn’t to say it never makes sense though – as I said earlier, some situations where the SharePoint moving pieces really have to sit next to the other lists/libraries/pages the end user has would be examples. I think Doug is actually saying as much when he talks about Building Traditional SharePoint Collaboration Solutions with the App Model – it’s just that I might be more optimistic that the isolated model works for more scenarios than he is. Time will tell.

Further reading:

17 comments:

Hrayr said...

Hi Chris,
Thanks for the great article.

A quick and simple question please.

When app web is created, is it exact mirror of host web? I mean, if i know that host web contains some "Projects" list with some data in it, then can i get data of this list via ClientContext(which is app web itself), or i need to make another client object or cross domain call to host web to get this list/data?

Wes said...

Another great article, one thing I think Doug's approach might make sense for is around search. From current understanding search can't see inside app webs. So scenarios where an App contains a doc lib with data this is hidden from search. It would be great if your series also talks to the approaches to search within the app data and any considerations like that.

Chris O'Brien said...

@Wes,

Quite true. Search cannot see inside app webs, at least, as far as my testing goes.

If the app's data needs to show up in search results someone (either main search results or some specific search implementation), then the data may indeed need to be in the host web.

It's a good scenario. I think there might be other options though. I'll definitely give it some thought as the series progresses.

Thanks,

Chris.

Chris O'Brien said...

@Hrayr,

No, the app web *contents* are not a mirror of the host web - no content is brought across. So, if you need to access your "Projects" list in the host web from a page in the app web, then you'd need to make a cross-domain call with SP.RequestExecutor.

HTH,

Chris.

Unknown said...

Chris,
Thanks for the kind words! Just a nit... You don't need Full Control to provision, just Manage. You can't submit an App that requires full control to the marketplace, but you can submit one that requires Manage.

I do agree, however, that they don't seem to want you to go nuts on the host web for marketplace apps and I would be surprised if they would approve it even though the current guidelines don't prohibit the approach. However, that is what the administrator side-install is for.

--Doug

Chris O'Brien said...

@Doug,

Thanks, that's a useful clarification! I'll update the main body text when I can.

Look forward to continuing the discussion :)

Chris.

Unknown said...

Hi Chris, thanks for the info, I wanted to ask for the code to persist the HostUrl in a cookie and also wanted to ask for the source of your TimeTracker app because it seems much more real world than the MS Samples. Thanks in advance.

Nik Patel said...

Extremely well written thoughts.. Loved this article. I agree, provisioning lists and libraries in host web to build collaborative solutions defeats the purpose of the Apps. Apps should be built for independent, scenario-based applications in SharePoint. And, belive me, there are plenty of them can replaced by Apps Model..

Servé Hermans said...

Chris, most of your articles are often wake up calls for me. Very useful so thanks for sharing your thoughts and views.

I certainly agree with your viewpoint on app webs should not have full control on host webs. It is a bit similar to the full proxy trusts we had earlier on with the sandbox solutions.

It almost looks like people try to find ways of doing more with SharePoint than the SDK shows, which is not a bad thing but often leads to HTTP handlers, declaritive tricks, proxies, control adapaters, client side injections and custom libraries.

Well, in the end those things need to be rewritten when the next upgrade is happening. And there it is: the essence of app webs almost maps to 'can we update the cloud services without having to think about custom solutions on top of SharePoint'.

Anyway, thanks again for your series. Like always you write in a way everybody understands and that I appreciate.

Regards, Serve Hermans
hermansberghem.blogspot.com

Chris O'Brien said...

Thanks all for the comments.

@Scott - sure thing. I'll post the source code over the next couple of evenings.

Chris.

KS said...

Chris, great blog. I have tested with the RTM install. I saw the same bug as you mentioned in the blog. It seems like your workaround will be still be around for a while. Thank you!

Chris O'Brien said...

@KS,

Yes, it is the same deal in RTM unfortunately. I'm about to publish a specific article on this, including the code I came up with. I had a meeting recently with the Microsoft team who build the Visual Studio tools for SharePoint, and we talked about this - at some point in the future, Visual Studio may automatically inject some code to deal with this, but for now individual developers will need to implement their own workarounds (either persist the URLs in a cookie or on the server somewhere).

It's NOT specifically viewed as a bug, though personally I'm still not sure about that classification ;)

Thanks,

Chris.

Manillaice said...

So is Sandbox solutions still a good approach for cases where you need to provision lists in the host web? for example. I need a deploy a few lists, but i want the lists to be searchable, and i dont want to have to recreate the app page to bring back the left navigation, top navigation.

Is it then best to use Sandbox to provision the lists? Keeping in mind that farm solutions are not possible in this example?

Chris O'Brien said...

@Fil,

Yes, a sandboxed solution is definitely still an appropriate solution for these cases. The one thing I'd say is that it's probably a good idea to avoid custom SharePoint server-side API code in a sandboxed solution - it's clear Microsoft is trying to move such code off the SharePoint box.

Provisioning lists etc. is something I expect sandboxed solutions to be used for commonly going forward though.

HTH,

Chris.

Chris O'Brien said...

@All,

Just one update to the comment thread here - it appears that there were some changes at some point (March 2013 update?) which means that Full Control is required for many actions, not Manage Web (as per Doug's earlier comment). I re-edited the article to this effect.

Thanks,

Chris.

Paul Culmsee said...

Very very impressive work here. Thanks Chris this helped me a lot...

Poolio said...

Hi Chris, thanks for the awesome series. I don't know when this changed (appreciate this thread is a couple of years old now), but it seems content stored in the app web does now get crawled, and appears in search results in the host web, search centre, etc. This is great as, for me, the search issue was one of the main drivers for creating content in the host web.

Cheers,
Anthony