Friday, January 31, 2014

SPRemoteAPIExplorer Visual Studio Extension Updated (Version 1.1)

Technorati Tags: ,,,,

Last month I announced the new SPRemoteAPIExplorer Visual Studio extension which enables you to determine what classes, methods and properties are available when using  the SharePoint REST, CSOM and JSOM API. The extension makes it easy to see what methods are available across the four different flavors (Managed, Silverlight, Javascript, REST) of the SharePoint 2013 remote API SPRemoteAPIExplorer. In this post I will tell you about a few new features added to the extension to help clear up some confusion with how Microsoft implements this REST API. The confusion arises from the fact that some methods cannot be called by name when using REST. Microsoft labels these types of methods as “IntrinsicRestful”. I will try to explain the purpose around these types of methods and how the extension can help you use them.  Another problem with the REST API is that some methods are allowed to be called but there parameters are not OData compatible.  Finally, I will explain the usefulness of the new method property “ServerName” and how it can help you POST objects with REST.

What exactly is “InstrinsicRestful” and why is it there in the REST API?

Below is a picture of SPRemoteAPIExplorer displaying the properties of the Create method of the Microsoft.SharePoint.SPListCollection. In version 1.1 the property grid will show a new property called “IntrinsicRestful”. If this is set to true then the method cannot be called using the URL endpoint resource. This is because the rest request processor assumes you are calling this method when ever you are posting to the collection resource.

Trying to call this method using a post like “http://servername/_api/web/lists/create” will result in a “resource not found error”.

An example of the correct way of calling this method is below.

function createList() {
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
url = appweburl + "/_api/SP.AppContextSite(@target)/web/lists?@target='" + hostweburl + "'";

$.ajax({
url: url,
type: "POST",
data:JSON.stringify({
'__metadata': { 'type': 'SP.List' },
'BaseTemplate': 101,
'Description': 'Rest Created2',
'Title': 'RestDocuments2'
}),
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"X-Http-Method": "POST",
"content-type": "application/json;odata=verbose"
},
success: function (data) {
var d = JSON.parse(data.body);

},
error: function (err) {
alert(JSON.stringify(err));
}
});

}

The rest request processor determines this is a post to a SPListCollection then gets the intrinsic restful method Create and uses this method to send the data to the object model. The processor uses the “Create_Client”  method listed in the “ServerMethod” property to call on the SPListCollection object. Knowing what method is called on the object model side can be useful when you want to use a reflection tool to debug the code. (Yes you can step through Microsoft’s code—Future Post ).


Another thing to be aware of when using these types of methods is that the parameter types are not always accurate. Below SPRemoteAPIExplorer shows the properties for the “parameters” argument to the create method. The “Parameter Type” for this argument is listed as “Microsoft.SharePoint.SPListEntityData”. Normally you would use this as the “__metadata” parameter “{‘type’: ‘SP.SPListEntityData’}”. Unfortunately, this would never work since this type is not defined in any of the server stubs (model). It is defined in the server side object model and is used as the argument to the “Create_Client” method.  A rule of thumb when you encounter a “xxxEntityData”  parameter type is to just use the actual object type it creating, in this case SP.List. The properties are the same and the rest request processor just maps the json properties to the SPListEntityData object when calling the server object model.



So why does the REST API have “IntrinsicRestful” methods? My theory is for the support of batch operations. Many of the collections exposed in Microsoft.SharePoint have a “Create” method which is listed as “IntrinsicRestful”.



  • SPViewCollection
  • SPFieldLinkCollection
  • SPFieldCollection
  • SPGroupCollection
  • SPFolderCollection
  • SPListItemEntityCollection

It would be nice if the SharePoint REST API supported posting and creating multiple entities in one request. It would cut down on the number of requests and responses making it a much more scalable API. The humorous aspect of this is that the OData specification does support sending multiple entities as collections. OData Representiting JSON collections. Of course you may also know these as “feeds”. The SharePoint REST API does support sending of collections as arguments for things such as “Select Properties” in a search postQuery request.  As an experiment I attempted to send a collection of multiple SP.List entities in one call using the OData standard of using the “results” parameter.

function createMultipleLists() {
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
url = appweburl + "/_api/SP.AppContextSite(@target)/web/lists?@target='" + hostweburl + "'";

var newLists = [];
newLists.push( {'__metadata': { 'type': 'SP.List' },
'BaseTemplate': 101,
'Description': 'Rest Created8',
'Title': 'RestDocuments8'});
newLists.push( {'__metadata': { 'type': 'SP.List' },
'BaseTemplate': 101,
'Description': 'Rest Created9',
'Title': 'RestDocuments9'});


$.ajax({
url: url,
type: "POST",
data: JSON.stringify({
'results': newLists,
"__count": "2"
}),
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"X-Http-Method": "POST",
"content-type": "application/json;odata=verbose"
},
success: function (data) {
var d = JSON.parse(data.body);

},
error: function (err) {
alert(JSON.stringify(err));
}
});

}

Of course this attempt failed with the error “argument found with no type defined”. This is due to the fact that the OData json deserializer used in the rest request processor will only look for a single entity. However, there is a “feed” type OData json deserializer which is used only internally by SharePoint when reading responses from BCS external sources.


OData Parameters Make a Big Difference for REST Support


Recently, I was trying to get REST to support web part configuration. Code below shows how to get a particular web part property using REST.

function getWebPartPropety(){
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));

var urlTemplate = appweburl + "/_api/SP.AppContextSite(@target)/web/getfilebyserverrelativeurl('/shared documents/forms/allitems.aspx')/GetLimitedWebPartManager(0)/webparts/getbyid('{f973f714-54bd-43bd-97d8-80ac6e70116e}')/webpart/properties?@target='" + hostweburl + "'";
$.ajax({
contentType: 'application/json',
url: urlTemplate,
type: "GET",
headers: {
"Accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val()
},
success: function (data) {
var result = data.d.qID;
},
error: function (err) {
alert(JSON.stringify(err));
}
});

}

This works. So looking at the SPRemoteAPIExplorer I determined the the web part properties is a Microsoft.SharePoint.SPPropertyValues object. This object has a SetFieldValue method which is marked that it is supported by REST. Unfortunately it is not.

function saveWebPartProperties() {
appweburl = decodeURIComponent(getQueryStringParameter('SPAppWebUrl'));
hostweburl = decodeURIComponent(getQueryStringParameter('SPHostUrl'));
//setfield value cannot be invoked since the value parameter is invalid.
var urlTemplate = appweburl + "/_api/SP.AppContextSite(@target)/web/getfilebyserverrelativeurl('/shared documents/forms/allitems.aspx')/GetLimitedWebPartManager(0)/webparts/getbyid('{534b28c9-7186-4eef-ac7a-dbe2330e9c87}')/webpart/properties/setfieldvalue?@target='" + hostweburl + "'";
$.ajax({
contentType: 'application/json',
url: urlTemplate,
type: "POST",
data: JSON.stringify({
'fieldName': 'qID',
'value': '2'
}),
headers: {
"accept": "application/json;odata=verbose",
"content-type": "application/json;odata=verbose",
"X-HTTP-Method": "MERGE",
"X-RequestDigest": $("#__REQUESTDIGEST").val(),
"IF-MATCH": "*"
},
success: function (data) {
var result = data.d.results;
},
error: function (err) {
alert(JSON.stringify(err));
}
});



}

The problem with the “SetFieldValue” method not working is that the “value” parameter is marked as invalid. Using the SPRemoteAPIExplorer you can see that  the “OData Type” property is set to “Invalid”. Even if the server stub marks the method callable by REST, the rest request processor will still reject the call based on the fact that the “value” parameter is marked as an invalid OData Type.



So in version 1.1 of the SPRemoteAPIExplorer the extension will mark the method’s REST property to false if any of the method’s parameters have an invalid OData Type. This will save you a lot of time.



Knowledge of SharePoint’s REST API is Getting Better and Better


Version 1.1 of SPRemoteAPIExplorer adds features to help you understand the quirkiness of the SharePoint REST API. The intrinsic restful aspect of the API just adds to the confusion tempting developers with the hope of batch processing capabilities. I hope that Microsoft improves the the scalability of the API soon and implement what many REST services can support already. It would also be nice if the server stubs would designate methods as not callable from REST if any of the parameters are OData invalid. To allow the method to be called only to have an error be thrown on the parameter seems rather unproductive leading developers down confusing rabbit holes. Finally, as I use this extension more and more I continue to discover problems with the complex types used as parameters. The use of SPListEntityData as the type for creating a list and then having the rest request processor throw an error because it is not defined in the model leads developers to think such methods do not work. REST API requires developers to experiment and I hope this tool will help you do just that.  If there are features you would like to see added to this tool then please feel free to post your request here or on the Visual Studio Gallery.