Using REST Services with a Provider Hosted App in SharePoint 2013
Today's topic seems straightforward enough, right? There's lots of documentation all over the interwebs about how to do this so should be a piece of cake. Well, as it turns out, when I did this a couple of weeks ago I found that there is a lot of missing information, misleading information, and in some cases incorrect information out there about how to do this. So I'm going to describe here the main points of how to get this to work and try and clear up a few gaps and what I call "tribal knowledge" about what is needed to make this work.
Let's start by considering the scenario - you want to use a provider-hosted app to connect to SharePoint and retrieve data via search. Now, by definition a provider hosted app is going to exist in a different domain from your SharePoint site where you are going to execute the query. This introduces the first problem: there are several docs out there that show REST calls being made using jQuery. Guess what - that's not going to work. Why not? Because your app is in a different domain from your SharePoint site, jQuery will fail by default with a "no transport" error. Steve's Seemingly Random Yet Useful Tip #1 - don't bother configuring the cross domain property of your AJAX query with jQuery; it will still fail when you get over to SharePoint land.
Okay, so we determine that we're in a different domain, then that must mean that we need to use the cross domain library that SharePoint offers in order to make this query work, right? So you follow the instructions to modify your AppManifest.xml, you change your javascript to use the SP.RequestExecutor object, and yet your request fails again. What's the problem here? Well, you can't use the host web Url with SP.RequestExecutor, you must use an App Web. But wait, provider-hosted apps don't have App Webs, so what gives? Well, the fact of the matter is you need to have your provider-hosted app create an App Web so that you can hook it up with the SP.RequestExecutor object. Steve's Seemingly Random Yet Useful Tip #2 - add an artifact to your SharePoint Application (not the web application that is your provider-hosted web app) that will force an App Web to be created. In my case I just added a custom site column. I don't do anything with it, I don't refer to it ever again, I just add it to the project, and that forces an App Web to be created. That allows me to use this tiny piece of code to get the App Web Url and pass it to my SP.RequestExecutor constructor: var appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
There's also one other thing worth noting here. When you want to use the cross domain library, you need to modify the AppManifest.xml by changing this:
<AppPrincipal>
<RemoteWebApplication ClientId="*" />
</AppPrincipal>
To this:
<AppPrincipal>
<Internal AllowedRemoteHostUrl="~remoteAppUrl"/>
</AppPrincipal>
Well guess what happens when you do that? If you also want to use CSOM in your code behind with a high-trust app, you will no longer be able to do so. Your app won't have the clientID it requires to make the connection to your SPTrustedSecurityTokenIssuer, so all of your CSOM calls will fail with authentication errors. Steve's Seemingly Random Yet Useful Tip #3 - it really does you no good in this case to make those changes to your AppManifest.xml to add support for the cross domain library; you're better off just leaving it as is so you can also use CSOM and server-side code if needed.
Okay, so now we're getting somewhere. The last important thing to understand here though is that when you go to run your code, you are going to have a very sub-optimal user experience by default. What do I mean by that? Well when your code executes, your REST call is going to a different domain. What you should expect to happen is that the end user then will get prompted for their credentials, even when using Windows auth. This of course is a terrible thing to have happen - a user launches an app, they see a blank page and suddenly it's asking for their Windows username and password. Not only is it a jarring experience, but if you're paranoid at all, the last thing you're going to do is give your credentials when a blank browser window randomly pops up in front of you. Steve's Seemingly Random Yet Useful Tip #4 - (this assumes Windows auth because it's simpler for now) - remember that your REST call is going to be processed by your App Web; to avoid those authentication prompts, add *.yourAppWebDomain.com to your list of Intranet Sites in IE. That stops the authentication prompts from occurring and you get a seamless end to end user experience.
Finally, here's an example of what my javascript actually ended up looking like to make the call to my app web from my provider-hosted app:
function getSearchInfo()
{
try
{
//use the app web for the search endpoint
var appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
//create the Url for getting the info, needs to look like this:
//https://sp5.vbtoys.com/_api/search/query?querytext='Author:"Steve+Peschka"'
var searchHost = appweburl + "/_api/search/query";
//create the query terms
var qry = "'Author:\"" + $("#authorName").html() + "\"'";
//create the concatenated query url
var qryUrl = searchHost + "?querytext=" + qry;
//initialize the RequestExecutor with the app web Url
var executor = new SP.RequestExecutor(appweburl);
//issue the query
executor.executeAsync(
{
url: qryUrl,
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success: onSearchSuccess,
error: onSearchError
}
);
} catch (e)
{
//do something here
}
}
Comments
Anonymous
January 01, 2003
@Son Dambi, the final code does not use the CDL changes to the app manifest - that was the point. Making that modification did break CSOM; not making that modification did not break JS calls.Anonymous
January 01, 2003
The comment has been removedAnonymous
April 21, 2013
II think that with a provider hosted app, it should be easier to use the ASP.NET MVC rather than client JavaScript.Anonymous
August 29, 2013
Something in this post gets confused near the end. It says "When you want to use the cross domain library, you need to modify the AppManifest.xml ,,," But later it says "it really does you no good in this case to make those changes to your AppManifest.xml to add support for the cross domain library; you're better off just leaving it as is ,,," OK. So if you "need" to change the app manifest to used C-DL and you're better off not changing it, then it follows that you are better off not using C-DL. But the final code example DOES use C-DL. So does this code work when the app manifest is not changed? If so, then you don't "need" to change the manifest to use C-DL after all.Anonymous
September 12, 2013
Thanks for your great information, the contents are quiet interesting.I will be waiting for your next post. <a href="www.allinonewebservice.com/">Web development Company in india</a>Anonymous
November 21, 2013
While debugging it is working fine,but When I try to deploy the SharePoint auto hosted app I am getting the error - Correlation Id: 4e2aa7aa-00be-4a4f-83bb-362a567c0e21 ErrorDetail: The remote hosting service is not configured. ErrorType: Configuration ErrorTypeName: Configuration ExceptionMessage: Microsoft.Office.SecureStoreService.Server.SecureStoreServiceTargetApplicationNotFoundException: Target application not found (application id: RemoteAppManagementInfo).Anonymous
July 07, 2014
I understand the point. I think you are missing mine: the word "need" is wrong in your sentence 'When you want to use the cross domain library, you need to modify the AppManifest.xml ..." Your point, in the end, is that we DON'T need to. See what I mean?Anonymous
September 15, 2014
What if my Provider-hosted app is not creating an app web where the proxy page is supposed to exist?Anonymous
September 18, 2014
The comment has been removed