Thursday 19 January 2017

End to End Scenario for using SharePoint REST API for Cross Domain List Operations from two SharePoint On Premises Farms

How to perform CRUD operations on a CROSS DOMAIN SharePoint 2013/2016 List from JavaScript using REST API. My target and source farms are within the Organization hence it does not cover the scenario where SharePoint Online is involved.

Recently I had a requirement to write data on a SharePoint List that resides on a separate SharePoint farm, I was asked to do this without writing Apps and possibly using JavaScript.

Initially I thought it’s a simple task and will not take more than a few minutes however I proved wrong very quickly. It is tricky and not much end to end help is available on line hence I thought of writing this blog.

Business Requirement:

I have a list named “oAuthTestList” in http://sharepointtesting.xxxx.com/xxxx/SharePoint/oAuth and I should be able to write into this list from a Page that is residing on a different SharePoint Farm http://sharepointdev.xxxx.com/sand/xxxx

The Page can have a Content Editor Web Part or Script Editor but now Server Side code. 

For this example I am assuming that SharePoint URLs for Source and Target Farms are as following:


I am also assuming that you have created a OAuthTest List in Target Farm. It has only one default column named as “Title”. Make sure you also make “ID” column visible on this list by modifying its default view. So now your list should show “ID” and “Title” columns.

Notice the first part of urls “sharepointdev” and “sharepointtesting”, it makes difficult to perform CRUD operations as SharePoint considers them as Cross Domain calls.

I have following two solutions to go without writing an app:

  1.  Using CSOM
  2.  Using REST API

Following is quick examples how to use both of them.


1. Using CSOM:

a.       I created a HTML File and a JavaScript File and placed them into SiteAssets document library in Source Farm (i.e. http://sharepointdev.xxxx.com/sand/xxxx ).
b.       I created a JavaScript file and name it as CSOMCreateAnItemInSharePointList.js. Following is its code.

function PerformListOperations() {
    var clientContext = new SP.ClientContext( "http://sharepointtesting.xxxx.com/teams/SharePoint" );
    var oList = clientContext.get_web().get_lists().getByTitle('OAuthTest');

    var itemCreateInfo = new SP.ListItemCreationInformation();
    this.oListItem = oList.addItem( itemCreateInfo );

    oListItem.set_item('Title',$('#Title').val());  
    oListItem.update();
    clientContext.load(oListItem);
    clientContext.executeQueryAsync(onPerformListOperationsSuccessonPerformListOperationsFail);
}

function onPerformListOperationsSuccess() {
    alert("Successfully created an item using CSOM...");
}

function onPerformListOperationsFail(sender, args) {
    alert('Failed to create. Error:' + args.get_message());
}

$(document).ready(function () {   
    $("#CreateButton").click(function () {                          
            SP.SOD.executeFunc('sp.js', 'SP.ClientContext'PerformListOperations);
    });    
});

c.       I created an html page and named it as “CSOMCreateAnItemInSharePointList.html”. Following is its code:

<!DOCTYPE html>
<html>
<head>
    <title></title>
     <meta charset="utf-8" />
    <link rel="stylesheet" href="/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
     <script src="http://sharepointdev.xxxx.com/xxxx/SiteAssets/CSOMCreateAnItemInSharePointList.js"></script>
    <script src="oAuthCreateOAuthTest.js"></script>   
</head>
<body>
    <div id="ListContainer"></div>
    <table>  
     
            <tr>
                <td>Title:</td>
                <td><input id="Title" type="textbox" value="Type new title text here"/></td>
            </tr>
            <tr>
                <td><input id="CreateButton" type="button" value="Create" /></td>                            
            </tr>
      
    </table>   
</body>
</html>

d.  Copy both the files in SiteAssets document library on Source SharePoint Farm.
e.  Now add a SharePoint Web Part Page on Source SharePoint Farm and add a Content Editor Web Part and configure the Path of CSOMCreateAnItemInSharePointList.html as created above.
f.  That’s all now you can add a list item by providing title’s text value. Each time you click on Button it will add a new item in the List.

      2. Using REST API

a.  It’s the tricky part and I used this for my solution.
b.  Again I created one HTML file and named it as “oAuthCreateOAuthTest.html” with following code:

<!DOCTYPE html>
<html>
<head>
    <title></title>
     <meta charset="utf-8" />
    <link rel="stylesheet" href="/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
     <script src="http://sharepointdev.xxxx.com/sand/xxxx/SiteAssets/oAuthCreateOAuthTest.js"></script>
    <script src="oAuthCreateOAuthTest.js"></script>   
</head>
<body>
    <div id="ListContainer"></div>
    <table>
        <thead>
            <tr>
                <td>Item ID (leave as it is for "Create"):</td>
                <td><input id="ItemId" type="textbox" value="1"/></td>
            </tr>
            <tr>
                <td>New Title Value :</td>
                <td><input id="newTitle" type="textbox" value="Type New Title"/></td>
            </tr>
            <tr>
                <td><input id="CreateButton" type="button" value="Create" /></td>
                <td><input id="UpdateButton" type="button" value="Update" /></td>               
            </tr>
        </thead>
    </table>   
</body>
</html>

c. And I created on JavaScript file named as oAuthCreateOAuthTest.js. Following is its code.

var
 _listItems=[],
 _options = {
     listName: "oAuthTestList",
     container: "#ListContainer"
 };
             
function WriteListDataToHtml(data, listName) {
    _listItems = data.d.results;
    var table = $("<table style='width: 60%;'>" +
                        "<tr style='color: maroon;background-color: yellow;font-weight:bold;'>" +
                            "<th>ID</th>" +
                            "<th>Date</th>" +                              
                            "<th>Title</th>" +
                        "</tr>" +
                  "</table>");                                          

    $.each(_listItems, function (index, item) {
        var tr = '<tr>' +
                   '<td>' + item.ID + '</td>' +
                   '<td>' + item.Created + '</td>' +                       
                   '<td>' + item.Title + '</td>' +
                '</tr>';
        table.append(tr);
    });
    $(_options.container).html(table);
}
function AddListItem(formDigestValue,appUrl, newTitle)
{
    var jsonMetaData = { __metadata: { type: 'SP.Data.OAuthTestListListItem' }, Title: newTitle };
    $.support.cors = true;      
                       
    $.ajax({
        url: appUrl,
        type: "POST",
        contentType: "application/json;odata=verbose",
        headers: { "accept": "application/json;odata=verbose", "x-requestdigest": formDigestValue, "If-Match": "*" },
        data: JSON.stringify(jsonMetaData),   
              
        xhrFields: { withCredentials: true },                         
        success: function (data) {
            if (data.d) {              
                RenderListData();
            }
        },
        error: function (xhr) {
            alert(xhr.status + ': ' + xhr.statusText);
        }
    });
}

function ReadListItem(formDigestValue, appUrl, newTitle) {
    var jsonMetaData = { __metadata: { type: 'SP.Data.OAuthTestListListItem' }};
    $.support.cors = true;
    //items?$filter='Internal Name of the column' eq "Some value"
    $.ajax({
        url: appUrl,
        type: "GET",
        contentType: "application/json;odata=verbose",    
        headers: { "accept": "application/json;odata=verbose"},       
       
        xhrFields: { withCredentials: true },
        success: function (data) {
            if (data.d) {              
                UpdateListItem(data.d.__metadata.etag, formDigestValue, appUrl, newTitle);
            }
        },
        error: function (xhr) {
            alert(xhr.status + ': ' + xhr.statusText);
        }
    });
}

function UpdateListItem(etag, formDigestValue, appUrl, newTitle) {
    var jsonMetaData = { __metadata: { type: 'SP.Data.OAuthTestListListItem' }, Title: newTitle };
    $.support.cors = true;

    $.ajax({
        url: appUrl,
        type: "POST",
        contentType: "application/json;odata=verbose",
        headers: { "accept": "application/json;odata=verbose", "X-Http-Method": "MERGE", "x-requestdigest": formDigestValue, "If-Match": etag},
        data: JSON.stringify(jsonMetaData),
        
        xhrFields: { withCredentials: true },
        success: function (data) {
            alert("Successful");
            RenderListData();
        },
        error: function (xhr) {
            alert(xhr.status + ': ' + xhr.statusText);
        }
    });          
             
   
  
}

function UpdateAListItem(listName, itemId, newTitle) {
    var formDigest;
    var hostUrl = "http://sharepointtesting.xxxx.com/xxxx/SharePoint/oAuth";   
    var appUrl = hostUrl+"/_api/web/lists/getbytitle('"+_options.listName+"')/items("+itemId+")";
    $.support.cors = true;
    $.ajax(
     {
         url:hostUrl + "/_api/contextinfo",
         type: 'POST',                    
         xhrFields: { withCredentials: true },
         dataType:"json",
                                 
         contentType: "application/x-www-url-encoded",
         headers: {
             "accept": "application/json;odata=verbose"
         },
         success: function (data) {
             if(data.d){
                 formDigest = data.d.GetContextWebInformation.FormDigestValue;
                 ReadListItem(formDigest, appUrl, newTitle);           
             }                   
         },
         error: function (err) {
             alert(xhr.status + ': ' + xhr.statusText);
         }
     });      
}

function CreateAListItem(listName, newTitle) {
    var formDigest;
    var hostUrl = "http://sharepointtesting.xxxx.com/xxxx/SharePoint/oAuth";
    var appUrl = hostUrl + "/_api/web/lists/getbytitle('" + _options.listName + "')/items";
   
    $.support.cors = true;
    $.ajax(
     {
         url: hostUrl + "/_api/contextinfo",
         type: 'POST',
         xhrFields: { withCredentials: true },
         dataType: "json",
         
         contentType: "application/x-www-url-encoded",
         headers: {
             "accept": "application/json;odata=verbose"
         },
         success: function (data) {
             if (data.d) {
                 formDigest = data.d.GetContextWebInformation.FormDigestValue;             
                 AddListItem(formDigest, appUrl, newTitle);
             }
         },
         error: function (err) {
             alert(xhr.status + ': ' + xhr.statusText);
         }
     });
}

function RenderListData() {
    var hostUrl = "http://sharepointtesting.xxxx.com/xxxx/SharePoint/oAuthh";
    var appUrl = hostUrl + "/_api/web/lists/getbytitle('" + _options.listName + "')/items?$select=ID,Title,Created&$top=20&$orderby=Created desc";
    $.ajax(
    {
        type: "GET",
        contentType: "application/json; charset=utf-8",
        url: appUrl,
        xhrFields: { withCredentials: true },
        
        processData: false,
        dataType: "json",
        headers: {
            "Accept": "application/json; odata=verbose"
        },
        success: function (data) { WriteListDataToHtml(data, _options.listName); },
        error: function (error) {
            console.log("Error in getting List: " + _options.listName);
            $(_options.container).html("Error retrieving your " + _options.listName + ".");
        }
    });
}

$(document).ready(function () { 
    $("#UpdateButton").click(function () {
        var itemId = $("#ItemId").val();
        var newTitle = $("#newTitle").val();
        UpdateAListItem(_options.listName, itemId, newTitle);
    });

    $("#CreateButton").click(function () {
        var itemId = $("#ItemId").val();
        var newTitle = $("#newTitle").val();
        CreateAListItem(_options.listName, newTitle);
    });
    RenderListData();
});


d.       Place both the files (oAuthCreateOAuthTest.html and oAuthCreateOAuthTest.js) in “SiteAssets” document library in source SharePoint Farm.
e.       Now let’s create a SharePoint Page in the source SharePoint Farm and add a Content Editor web part. Configure the URL of oAuthCreateOAuthTest.html.
f.        Now you are ready to perform CRUD operations from a SharePoint Farm’s page to a List located on the Target SharePoint Farm.

g.       Please note it will work only on Internet Explorer.