Sharepoint 2013: Get Me Out Of Here (Part 2): Cross Domain AJAX Calls in Sharepoint Apps   7 comments

The Bottom Line

You can find Part I here.

We are using Sharepoint 2013 RTM. We will be looking at Sharepoint Hosted Apps in this post and we will deal with provider hosted apps in a later blog post. Out bottom line on Development Environments is we are using a developer ‘environment in the cloud’ from CloudShare. Our specific goal is the use the new, and sparsely documented, Sp.WebProxy Javascript function to do cross domain calls into REST Endpoints which are external to Sharepoint. We will not be discussing the esotaric hidden IFrame solution discussed in Solving cross-domain problems in apps for SharePoint.

Forget about WCF and using Custom Restful Web Services like we used in Sharepoint 2010.  Although this technic did work to add proxy access to external data sources, the process was poorly documented, highly technical and fragile.  Are basic need is to use Sharepoint 2013 and to access data from other servers which we control which have our data.  For important reasons these do not fit into the B?? model and must be accessed either through proxy methods (see Part I of this series) or with cross-domain AJAX calls from the client.   When we transitioned to Sharepoint 2013 after our brief marriage to Sharepoint 2010 we were very excited about the whole App concept or  thing or whatever it is.  We decided to write some, just as soon as we could figure out what they are, how to write them and how to  set up a development environment.  Our bottom line on Sharepoint Apps.  They come in three flavors: auto-hosted, Sharepoint Hosted and Provider Hosted. 

Create a Development Environment

Wow.  Look at those specifications!  Eight Gig plus of RAM, 64 bit Quad Core Processor, Visual Studio 2013. Page after page of unclear and contradictory documentation; most it for the Preview not RTM.  Where did I put that old propeller beany I used to wear in the 1990’s?  Ok, looks like we might be able to try this with our desktop machines (they are monster machines), but just in case we also start looking at VMware to build these suckers on. The basic MSDN documents:

Well this looks really hard and we may need to set this up more than once (looks like we could trash the system more than once getting things right).  This clearly is not a simple, focused product like SQL Server.  So we started looking for more help and setup tips.  Trashed my physical machine a couple of times, rebuild are taking a long time.  Shift to Hyper-V as MS suggests, still can’t quite get a system setup and still have to deal with the corporate AD.  Well, at least re-builds aren’t taking as long.  One of my co-workers takes a (physical) class.  When he returns he is pretty excited.  They used a cloud based configuration of Sharepoint 2013 (Not Sharepoint Online or Office 365 or whatever the folks at Redmond are calling the AZURE based system today) and was pretty happy.  We shift to using CloudShare.  They have a three server template with fully licensed Sharepoint 2013, SQL Server 2013 and a separate AD server; plus all the Microsoft software you can shake a stick at (Visual Studio, Office 2013, etc. at no extra cost).  Nice, not that expensive for a developer playpen and I can spin up an instance in about 4 minutes and can take snap shots of by way stations  during any radical reconfigurations so I can drop back to a stable version in about 15 minutes.  Access is via browser and for a development environment it all runs quickly.  We are happy again.

Get The APP Documentation and Sample Code

Hmm again. A lot of the stuff on MSDN and Technet are dated July, 2012 and are based on the preview drop and/or for the Azure based system of Sharepoint.  Not good.  Hey, how about Plurasight?  They seem to be jacked pretty directly into Microsoft.  They have a whole bunch of Sharepoint  courses, tons of Sharepoint 2010 courses, this must be helpful right, Sharepoint 2013 can’t be that big of a jump form 2010 can it? And where are those Sharepoint 2013 apps courses? Oh ok, here they are,  all dated 2012-11-05:

Twelve hours of Sharepoint 2013….stuff.  Part 6 is over three hours long.  Ok lets do it.  Installation courses are ok but they seem to be leaving a lot of stuff out (or perhaps we fell asleep at some point).  The blogs for Sharepoint App development often have the same problem, based on the preview or on the cloud based Azure version of Sharepoint and often (this really gets me) just re-writes of the same base line documents from MSDN. Here are some basic papers we used (Don’t blame us for the poor capitalization of the titles):

Create A Sharepoint Hosted App

The MSDN papers How to: Create a basic SharePoint-hosted app  should be enough to get you up and running on a basic low functionality Sharepoint hosted App.  Note this correction however.  Replace the lines of code from the paper:

function sharePointReady() {
    ctx = new SP.ClientContext.get_current();    $("#getListCount").click(function (event) {
        getWebProperties();
        event.preventDefault();
    });
    welcome();
}
With

$(document).ready(function () {

ctx = new SP.ClientContext.get_current();

$(“#getListCount”).click(function (event) {

       getWebProperties();

        event.preventDefault();

       });

     welcome();

});

Once you have a Sharepoint hosted app which will do ANYTHING(this may take a while), start a new project based on the papers:

Cross Domain AJAX Calls

I don’t care what lies you have been told before, you can make cross domain AJAX calls from within a Sharepoint hosted app, with javascript and they all involve calling directly or indirectly SP.WebProxy.invoke.  In the romantic technical documentation of MSDN this is defined as:

[ScriptTypeAttribute(“SP.WebProxy”, ServerTypeId = “{656a77c4-1634-415c-bf85-c6c0cb286e0e}”)] public static class

WebProxy

WebProxy has a single method[RemoteAttribute] public static ClientResult

Invoke

( ClientRuntimeContext context, WebRequestInfo requestInfo


and

WebRequestInfo

is defined as:[ScriptTypeAttribute(“SP.WebRequestInfo”, ValueObject = true, ServerTypeId = “{71aa825d-bc12-422d-a177-d2e63fe68cd9}”)] public class WebRequestInfo : ClientValueObject


and has a plethora of

properties and methods

.


We use these objects and methods by including the Sharepoint JavaScript libraries (SP.Runtime.js) in our Sharepoint APP.


Call Cross Domain Using SP.WebProxy.invoke

Setting up the call in JavaScript:

var context = SP.ClientContext.get_current();

var request = new SP.WebRequestInfo();

//Set the Url, HTTP Method and Accept Headers

//We could also include OAuth information and parameters.

request.set_url(

http://uvo1vnoxg7xeb0u4dm1.env.cloudshare.com/WebAPI005/api/values”

          );

request.set_method(“GET”);

request.set_headers({ “Accept”: “application/json” });

var response = SP.WebProxy.invoke(context, request);

      context.executeQueryAsync(successHandler, errorHandler);

  • Http Request:

POST http://app-8520ffa3ec708b.sp2013apps.com/DLR002/_vti_bin/client.svc/ProcessQuery HTTP/1.1

Accept: */*

X-Requested-With: XMLHttpRequest

Content-Type: text/xml

X-RequestDigest: 0x1CB7B3E3FBBD705515A23DB5DEEDDF06FF5659232C5E1891205D2C10E5F772C13DE15FF53CC85FF76AA6552B4E5DA0C845C48F6C64DFFD825159A686B2E3561F,11 Feb 2013 18:13:54 -0000

Referer: http://app-8520ffa3ec708b.sp2013apps.com/DLR002/Pages/Default.aspx?SPHostUrl=http%3A%2F%2Fsp2013srv&SPLanguage=en%2DUS&SPClientTag=0&SPProductNumber=15%2E0%2E4420%2E1017&SPAppWebUrl=http%3A%2F%2Fapp%2D8520ffa3ec708b%2Esp2013apps%2Ecom%2FDLR002

Accept-Language: en-US

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)

Host: app-8520ffa3ec708b.sp2013apps.com

Content-Length: 719

Connection: Keep-Alive

Pragma: no-cache

Cookie: WSS_FullScreenMode=false

POST Body:

Response1

Notes:

This is a HTTP POST to _vti_bin/client.svc/ProcessQuery.  Note the checksum in X-RequestDigest and the Accept and Content-Type headers refer to the call to ProcessQuery and not to our ultimate endpoint (…WebAPI005/api/values).  The endpoint url and headers were packaged in WebRequestInfo and appear in the body of the POST in XML format.  In this simple call we are only defining a Method (GET) Accept-Type (application/json) and an Url (http://…WebApi005/api/Values).

A successful HTTP response might look like:

HTTP/1.1 200 OK

Cache-Control: private

Content-Type: application/json; charset=utf-8

Vary: Accept-Encoding

X-SharePointHealthScore: 0

SPClientServiceRequestDuration: 1753

SPRequestGuid: 36cafd9b-c9ea-1071-02d5-3352073a4f7c

request-id: 36cafd9b-c9ea-1071-02d5-3352073a4f7c

X-RequestDigest: 0x5ADEBB9D5BE8268193CA8B29902E1C16B1C11DC23CBD94B5FBD6E6A659D34D29765539025B8B34DFC3719279ED07B3306083414D88611E1D09000B30DACFDFB0,11 Feb 2013 18:13:55 -0000

X-FRAME-OPTIONS: SAMEORIGIN

X-AspNet-Version: 4.0.30319

X-Powered-By: ASP.NET

X-Content-Type-Options: nosniff

X-MS-InvokeApp: 1; RequireReadOnly

MicrosoftSharePointTeamServices: 15.0.0.4420

Date: Mon, 11 Feb 2013 18:13:56 GMT

Content-Length: 597

The body for our Restful Endpoint would look like:

Response2

The content-type response header specifies application/json and our data is also json. Note also that the content-type of the Body element is defined by the JSON response element ResponseBody[2].Headers.Content-Type. Note that the format of response body is dependent upon the sender.  A response for a Sharepoint list will have a different format than the WebAPI sender we are using here. See How to: Query a remote service using the web proxy in SharePoint 2013 for an example of processing Sharepoint output.

Success and Error Routines

function successHandler() {

if (response.get_statusCode() == 200) {

var ResponseBody;

              var thing1;

              var thing2;

ResponseBody = JSON.parse(response.get_body());

               thing1=ResponseBody[2].Body[0];

               thing2=ResponseBody[2].Body[2];

                   //Do Your Thing with each value

}

}

else {

var httpCode;

               var httpText;

               httpCode =    response.get_statusCode();

httpText = response.get_body();

               //Do your thing with the error response

}

}

       function errorHandler() {

          var httpText2=response.get_body();

         //Do your thing with the error response

}

Call Cross Domain Using JQuery AJAX to Call SP.WebProxy.invoke

Here is the same call but using JSON to call SP.WebProxy as an AJAX REST call:

var url = “http://uvo1vnoxg7xeb0u4dm1.env.cloudshare.com/WebAPI005/api/values”;

      $.ajax({

url: “../_api/SP.WebProxy.invoke”,

type: “POST”,

data: JSON.stringify(

{

“requestInfo”: {

“__metadata”: { “type”: “SP.WebRequestInfo” },

“Url”: url,

“Method”: “GET”,

“Headers”: {

“results”: [{

“__metadata”: { “type”: “SP.KeyValue” },

“Key”: “Accept”,

“Value”: “application/json;odata=verbose”,

“ValueType”: “Edm.String”

}]

}

}

}),

headers: {

“Accept”: “application/json;odata=verbose”,

“Content-Type”: “application/json;odata=verbose”,

“X-RequestDigest”: $(“#__REQUESTDIGEST”).val()

},

success: successHandler,

error: errorHandler

});

Note the X-RequestDigest Header setup which gets the Digest value directly from the ASPX Form element __REQUESTDIGEST and is required.

HTTP POST

POST http://app-8520ffa3ec708c.sp2013apps.com/DLR003/_api/SP.WebProxy.invoke HTTP/1.1

Accept: application/json;odata=verbose

Content-Type: application/json;odata=verbose

X-RequestDigest: 0x622ED77A91DC009DACC720FEEA25768E3208E3C9FF1F6B64F7094C6461BC59B5675BEF0267B4AFD0B7F854484484706EBB8DA42BDB0E91AE536B2BC57C478824,11 Feb 2013 19:22:00 -0000

X-Requested-With: XMLHttpRequest

Referer: http://app-8520ffa3ec708c.sp2013apps.com/DLR003/Pages/Default.aspx?SPHostUrl=http%3A%2F%2Fsp2013srv&SPLanguage=en%2DUS&SPClientTag=0&SPProductNumber=15%2E0%2E4420%2E1017&SPAppWebUrl=http%3A%2F%2Fapp%2D8520ffa3ec708c%2Esp2013apps%2Ecom%2FDLR003

Accept-Language: en-US

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)

Host: app-8520ffa3ec708c.sp2013apps.com

Content-Length: 292

Connection: Keep-Alive

Pragma: no-cache

Cookie: WSS_FullScreenMode=false

Authorization: NTLM TlRMTVNTUAADAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAAAAAAAFgAAAAAAAAAWAAAAAAAAABYAAAABcKIogYC8CMAAAAP0BK39LK2kSQ52HreNJOgaQ==

Note: the authentication is handled on the fly with NTLM negotiation.

JSON Content

Response4

Note here the call body is in JSON, since this is a typical JSON AJAX call to the Sharepoint REST subsystem and is used to pass the url, headers and any optional parameter values to the target Url.

HTTP Response

HTTP/1.1 200 OK

Cache-Control: private, max-age=0

Content-Type: application/json;odata=verbose;charset=utf-8

Expires: Sun, 27 Jan 2013 19:22:33 GMT

Last-Modified: Mon, 11 Feb 2013 19:22:33 GMT

Vary: Accept-Encoding

X-SharePointHealthScore: 0

SPClientServiceRequestDuration: 15009

SPRequestGuid: 24cefd9b-e96c-1071-02d5-32abbeb2cd48

request-id: 24cefd9b-e96c-1071-02d5-32abbeb2cd48

X-RequestDigest: 0xF7EEE4ED6914FDAD63C7C097117C21AEF182741021B51616C22A96D1CDD650A57C79D02D4C81F31B0EB2E956E6EC7254356CAB341E94F1B155F4E1C5A6AD1866,11 Feb 2013 19:22:33 -0000

X-FRAME-OPTIONS: SAMEORIGIN

Persistent-Auth: true

X-AspNet-Version: 4.0.30319

X-Powered-By: ASP.NET

X-Content-Type-Options: nosniff

X-MS-InvokeApp: 1; RequireReadOnly

MicrosoftSharePointTeamServices: 15.0.0.4420

Date: Mon, 11 Feb 2013 19:22:48 GMT

Content-Length: 1395

Response Body:

Response5ab

Note that here when called this way out response data is found as

responseBody.d.Invoke.Body[0]

responseBody.d.Invoke.Body[0]

Strange but true.

Note also that no matter how you call the cross-domain endpoint you are getting back two status codes.  One status code of the SharePoint call itself and one status code, nested in the JSON response,  from the cross-domain endpoint.  I am still experimenting with this.

Next Steps

My next step with SP.WebProxy will be to include security information in the call and to process this information on the REST endpoint.  See You Then.

 

7 responses to “Sharepoint 2013: Get Me Out Of Here (Part 2): Cross Domain AJAX Calls in Sharepoint Apps

Subscribe to comments with RSS.

  1. Pingback: Share Point |

  2. I am not sure where you are getting your information,
    but good topic. I needs to spend some time learning more or understanding more.

    Thanks for magnificent info I was looking for this info for my
    mission.

  3. Interesting blogpost. I’m trying to connect to an external web.api within a SharePoint hosted app using the sp.webproxy with a basic authentication ‘token’ in the header of the SP.WebRequestInfo object and add the web.api url to the remote endpoints in the manifest. However I keep getting 401’s in return. In your last sentence you mentioned ‘include security information in the call’. Do you already got some tips and tricks?

  4. Pingback: Sharepoint 2013: Get Me Out Of Here (Part I): Full Trust Proxy | Cloud2013 Or Bust

  5. Pingback: Fix Express Errorhandler Json Windows XP, Vista, 7, 8 [Solved]

  6. Please assist in passing authentication token to header of sp.webproxy call. Please let me know if that is possible

  7. Pingback: Share Point - startachim

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: