Summary: Because of the significant inconsistencies in the SOAP Web Services, converting your calls to REST will be a different process for each of them. In this article, we’ll look at the real workhorse of SPServices: GetListItems.
When my mechanic works on my car, sometimes he tells me about some different tires or parts I might consider instead of what was on the car since I got it. It might be to improve performance or to give me a bit more life out of the car than I might get otherwise. (I have a good mechanic.)
As I’ve suggested in prior articles in the series, you can start moving individual calls from SOAP to REST anytime that you decide. If you’re still running an older version of SharePoint (no shame there), then it probably won’t be until you upgrade to SharePoint 2013. If you’re already on SharePoint 2010 or 2013, then you can start anytime.
When it comes time to converting some SOAP calls to REST calls, what will that process look like? In this article, we’re going to take a look at converting the workhorse of the SOAP Web Services: GetListItems.
GetListItems lets us read items from a list or library (virtually all content in SharePoint is stored in a list or library – a library is just a fancy list that is document-centric). Even basic information about users is stored in each Site Collection in the User Information List. GetListItems does a lot for us in SPServices-based development; it’s the most-commonly used function by just about every developer who uses SPServices and it sits underneath most of the value-added functions like SPCascadeDropdowns and SPDisplayRelatedItems.
The basic structure of the simplest GetListItems call is this:
$().SPServices({ operation: "GetListItems", listName: "Tasks" });
We make a call to SPServices, specify the GetListItems operation, and specify the list from which we want to get data. That’s about it. The results of this call will contain the items in the Tasks list (in the current site) as they are displayed in the Tasks list’s default view, which by default would be AllTasks.aspx.
Usually we’d have a bit more code wrapped around this call, maybe something like this:
var p = $().SPServices({ operation: "GetListItems", listName: "Tasks" }); p.done(function() { // Process the response });
Note that I’m using a promise above so that I know when the call has completed rather than making a synchronous call. More on that in a later article.
So what does the analogous REST call look like? It would be something like this:
$.ajax({ url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Tasks')/items", method: "GET", headers: { "Accept": "application/json; odata=verbose" }, success: function(data) { success(data); // If the call is successful, process the response }, error: function(data) { failure(data); // Do something with the error } });
Not so different when you get past the sides and trimming, right? The specifics of what we’re asking for in the REST call are all in the url object. The way REST works is that we pass information about what we want the server to do for us as parameters on the url. In this case, we have:
url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle('Tasks')/items",
This means:
- Go to the context of _spPageContextInfo.webAbsoluteUrl, which is the current site. In SPServices, I figure this out for you, or you can choose to specify a different site with the webURL parameter.
- Call the lists service: /_api/web/lists
- Select the Tasks list using its name: /getbytitle(‘Tasks’)
- And finally, get the items: /items
The rest of the REST call is just packaging and what we want to do with the result.
Where the two methods start to diverge more is when we start to request things in more specific ways, for example all of the outstanding tasks for a particular user. But there’s an imperfect isomorphism here. For virtually everything we can do with the SOAP Web Services, we can do the same thing in REST just by adding the analogous parameters to the REST call.
Here’s how some of the most common parameters in the request payload map from SOAP to REST:
SOAP Option(SPServices synonym) | REST | Comments |
ViewFields(CAMLViewFields) | $select | Choose the columns you would like to retrieve. With both SOAP and REST we get some data we don’t explicitly request, but by specifying only the columns we need we can reduce the payload sizes. |
Query(CAMLQuery) | $filter, $orderby | Specify which items in the list we would like to return and how we would like them sorted. |
RowLimit(CAMLRowLimit) | $limit | Say how many items matching the Query we would like to receive. In SOAP we can specify 0 to get all matching items; in REST we can omit the parameter to get all the matching items. Otherwise, we can specify any integer as the limit. |
ViewName(CAMLViewName) | NA | ViewName lets you choose the view you would like to get in the response. There’s no REST equivalent here. I’ve always discouraged using this option in SOAP because it’s too easy to change the view settings and cause unintended consequences. |
QueryOptions (CAMLQueryOptions) | NA | In SOAP, this lets us specify some options that change how the data is returned to us. For example, we can ask for all of the attachment URLs rather than just a Boolean which tells us that there are attachments. |
NA | $expand | This option in REST has no direct equivalent in SOAP. $expand allows us to indicate that we would like the values for a relationship – rather than just the indices – using a projection. This is important with Lookup columns and Person or Group columns. |
References:
As you can see, it’s really not that scary. It’s the equivalent of translating Spanish into French, but even easier since all of the words are in English (sorry rest of the world – I’m going to play American on this one).
Here’s a slightly more complicated example where I’m asking for items from a Workflow Tasks list.
First, the SOAP request:
And then the equivalent REST request:
Here’s how the two calls break down based on the table I showed above:
SOAP | REST | ||
CAMLViewFields | <Viewfields><FieldRef Name=’ID’/><FieldRef Name=’Title’/><FieldRef Name=’AssignedTo’/><FieldRef Name=’RelatedItems’/>
<FieldRef Name=’Priority’/> <FieldRef Name=’Status’/> <FieldRef Name=’DueDate’/> <FieldRef Name=’Author’/> </ViewFields> |
$select | $select=ID, Title, AssignedTo/Id, AssignedTo/Title, RelatedItems, Priority, Status, DueDate, Author/Id, Author/Title |
CAMLQuery | <OrderBy><FieldRef Name=’Created’ Ascending=’False’/></OrderBy> | $filter, $orderby | $orderby=Created Desc |
NA | $expand | $expand=AssignedTo,Author |
Note that in the SOAP call, I get the ID and Title (name) of each person or group column automatically when I request the column value. If I needed any more information about those people, I’d have to read it from another source, like the User Information List or User Profile. I also can specify a CAMLQueryOption that returns more of the basic information about users, like email address, SIP, etc.:
CAMLQueryOptions: "<QueryOptions><ExpandUserField>True</ExpandUserField></QueryOptions>"
In SOAP, most options that we pass go in the request payload. With REST, some of the options – for instance, what format we’d like the response to take – go in the request header.
If we don’t specify an accept parameter in the header, then we get XML in the response. If you’re used to parsing XML, then that might make you happy, but we usually want JSON these days.
To get JSON, we specify:
accept: application/json; odata=verbose
On premises in SharePoint 2013, this is the only option at the moment. In SharePoint Online (Office 365), if we don’t want all the metadata that comes along with the verbose option, we have a couple other options.
accept: application/json; odata=minimalmetadata
accept: application/json; odata=nometadata
The best way to understand the differences between these options is to try them out. When payload size matters (and it usually does), you should think about receiving less metadata to reduce the size of the responses.
References:
- JSON Light support in REST SharePoint API released
- OData Protocol JSON Format Standards Support Document
- REST API reference and samples
It’s the nuances that will trip us up. In SOAP, we can’t join lists together; we have to pull the data from each list and do the “join” on the client side. In REST we have what are called projections – using $expand – that let us do some lookups into other lists, for example to get information from a lookup list.
But back to GetListItems. Here are some examples of REST calls to get data back from a Tasks list showing some common parameter settings.
Get all tasks assigned to me
/_api/web/lists/getbytitle('Development Tasks')/items?$expand=AssignedTo&$select=Title,AssignedTo/Title&$filter=AssignedTo/Title eq 'Anderson, Marc'
Note that the format of your username will vary based on how you have things set up. Using an account may be a better choice, but I wanted to make the example clear.
Get all tasks that have not been completed, showing the status and the % complete
/_api/web/lists/getbytitle('Development Tasks')/items?$expand=AssignedTo&$select=Title,AssignedTo/Title,Status,PercentComplete&$filter=Status ne 'Completed'
Getting the data in these various ways is of course just the first step. Once you have the data, you’ll have to process it in some way in order to display it to the user, do calculations on it or whatever you’re trying to accomplish. That part of the exercise isn’t in the scope of this article, so you’ll have to look elsewhere for tricks for those pieces.
In the next article, we’ll look at how JavaScript promises come into play with SOAP and REST. I hope that you’ve been using promises with SPServices calls since I added support for them in version 2013.01, but if you haven’t, moving to REST is an excellent time to start doing so.
by Marc D Anderson via Marc D Anderson's Blog
No comments:
Post a Comment