The first thing I really like about SharePoint REST APIs is, we do not need any reference to client assemblies or JS libraries. The basic operations in REST API includes CRUD operations on lists. You can find the list of these end points on MSDN. And we can find so many examples on MSDN and other websites using REST APIs with jQuery $.ajax.
Here is why I’m not a big fan of $.ajax with SharePoint APIs, for two reasons.
I’ve used $.ajax for adding a list item and I thought it will be working fine.
var AddListItem = function(product) {
return $.Deferred(function (def) {
var data = {
__metadata: { type: 'SP.Data.ProductsListItem' },
Title: product.ProductName,
ProductNumber: product.ProductNumber,
Price: product.Price
};
$.ajax({
url: webUrl + "/_api/web/lists/getbytitle('Products')/items",
type: 'POST',
contentType: 'application/json;odata=verbose',
data: JSON.stringify(data),
headers: {
'Accept': 'application/json;odata=verbose',
'X-RequestDigest': $('#__REQUESTDIGEST').val()
},
success: function (data) {
def.resolve(data)
},
error: function (s,a,errMsg) {
def.reject(errMsg)
}
});
});
};
But I see a problem with POST requests. we need to include X-RequestDigest parameter in request and the Request digest value expires in 18 seconds(AFAIK). The code worked fine in my office365 environment. But not worked when I make a POST call after 18 seconds. The RequestDigest timeout was very less in one of my on-premise environments so the code was never able to make POST operations. returned error message like 403/401. So I removed $.ajax and used SharePoint cross domain JS library sp.requestexecutor.js to make REST calls.
var AddListItem = function (product) {
var executor = new SP.RequestExecutor(webUrl);
return $.Deferred(function (def) {
var url = webUrl + ";/_api/web/lists/getbytitle('Products')/items";
var data = {
__metadata: { type: 'SP.Data.ProductsListItem' },
Title: product.ProductName,
ProductNumber: product.ProductNumber,
Price: product.Price
};
executor.executeAsync({
url: url,
method: 'POST',
body: JSON.stringify(data),
headers: {
'Accept': 'application/json; odata=verbose',
'content-type': 'application/json;odata=verbose';
},
success: function(data){def.resolve(data)},
error: function(s,a,errMsg){def.reject(errMsg)}
});
});
};
Now, this code looks same like the code we use to make REST calls in Cloud hosted apps and it is same except the URL and url in SP.RequestExecutor() Constructor. The URL in cloud hosted apps looks like
appweburl +"/_api/SP.AppContextSite(@target)/web/lists/getbytitle('Products')/items?@target=" +hostweburl
We are not using the key Object of cross domain API “SP.AppContextSite” here and the URL is same as we use with $.ajax. The second Difference is we are creating ‘SP.RequestExecutor()’ to current site URL(not any appWebUrl). The benefit here we get is we need not to pass X-RequestDigest parameter. This cross domain library will take care of getting the Latest Request Digest from server(https://site/_api/contextInfo) and append it to Request. This code worked perfectly in all of my environments (Office 365 and OnPremise). You need to load /_layouts/15/sp.requestexecutor.js‘ to use this library.
In one of my projects we implemented a site collection which have list of community sub sites and you can follow / unfollow those communities from a WebPart at site collection level. And some of those communities are Private(with unique permissions). Assume you are using $.ajax to get the follow status / to follow / to unfollow. The code to get the status of Follow looks like.
var followingManagerEndpoint = webUrl + "/_api/social.following";// you can use rootSite Url here
function isFollowed() {
$.ajax( {
url: followingManagerEndpoint + "/isfollowed",
type: "POST",
data: JSON.stringify( {
"actor": {
"__metadata": {
"type":"SP.Social.SocialActorInfo"
},
"ActorType":SP.Social.SocialActorType.site,
"ContentUri":webUrl,
"Id":null
}
}),
headers: {
"accept":"application/json;odata=verbose",
"content-type":"application/json;odata=verbose",
"X-RequestDigest":$("#__REQUESTDIGEST").val()
},
success: function (responseData) {
stringData = JSON.stringify(responseData);
jsonObject = JSON.parse(stringData);
if (jsonObject.d.IsFollowed === true ) {
alert('The user is currently following the site.');
//stopFollowSite();
}
else {
alert('The user is currently NOT following the site.');
//followSite();
}
},
error: requestFailed
});
}
Assume that you are making this call to get the follow status of a private to which you do not have access. You cannot make a call with url as “webUrl + “/_api/social.following” because you do not have access to webUrl. So you can try rootSiteUrl in followingManagerEndPoint and pass the subsiteurl in contentUri parameter. It still thrown me an error(403 or 401, do not remember). The solution is to get rid of $.ajax and use SP.RequestExecutor(rootSiteUrl).
var LoadIsFollowed = function (documentOrSiteUrl, rootSiteUrl, actortype, successcallback, failcallback) {
var requestinfo = {
url: rootSiteUrl+ '/_api/social.following/isfollowed',
method: "POST",
body: JSON.stringify({
"actor": {
"__metadata": {
"type": "SP.Social.SocialActorInfo"
},
"ActorType": actortype,
"ContentUri": documentOrSiteUrl,
"Id": null
}
}),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose"
},
success: function (responseData) {
successcallback(responseData);
},
error: function (s, a, errMsg) {
failcallback(s, a, errMsg);
}
};
var executor = new SP.RequestExecutor(rootSiteUrl);
executor.executeAsync(requestinfo);
};
Similarly we can implement code to follow/unfollow the subsite even you do not have access to it.